Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50f08e996a | ||
|
|
0e023bd3fa | ||
|
|
4f7a5cfbde | ||
|
|
f24d306026 | ||
|
|
7b1086bdbc | ||
|
|
1c6dd1ca6a | ||
|
|
e497261789 | ||
|
|
06859f3464 | ||
|
|
6606f64a4b | ||
|
|
ac8936e238 | ||
|
|
adbc31f8d4 | ||
|
|
16b9a7702b | ||
|
|
2bae3cf9ed | ||
|
|
64128f37e0 | ||
|
|
83f8b67b20 | ||
|
|
b10cff294c | ||
|
|
32caa5a4b7 | ||
|
|
738b168689 | ||
|
|
e1fa3cf645 | ||
|
|
3ed64f9f22 | ||
|
|
94c9963e81 | ||
|
|
4c940c2088 | ||
|
|
50638e6dfa | ||
|
|
f081d89fb4 | ||
|
|
fd39434446 | ||
|
|
72349248ae | ||
|
|
02e525ab83 | ||
|
|
0950fc69f9 | ||
|
|
750fb49a37 | ||
|
|
68d569a6de | ||
|
|
3fd5b2ee55 | ||
|
|
e9b475a230 | ||
|
|
8720c18654 | ||
|
|
4daf0a15ba | ||
|
|
3474636e23 | ||
|
|
f0ff9a88a7 | ||
|
|
21028ec6f0 | ||
|
|
c4af1653ff | ||
|
|
250cbdde7c | ||
|
|
a966f3aeda | ||
|
|
3c02d55ebf | ||
|
|
4009e845cb | ||
|
|
37f2405ab3 | ||
|
|
8ec80a02b6 | ||
|
|
601f250882 | ||
|
|
019fee2cce | ||
|
|
d4e0898674 | ||
|
|
d642c9641d | ||
|
|
2ba8cb67c8 | ||
|
|
350dc0881e | ||
|
|
db1ef3b317 | ||
|
|
1c2696798c | ||
|
|
2b1c7f16cf |
@@ -0,0 +1,2 @@
|
|||||||
|
.git
|
||||||
|
web/vue/node_modules
|
||||||
@@ -23,18 +23,18 @@ _testmain.go
|
|||||||
*.test
|
*.test
|
||||||
*.prof
|
*.prof
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
log/*
|
log
|
||||||
data/*
|
data
|
||||||
conf/install.lock
|
conf
|
||||||
conf/app.ini
|
|
||||||
conf/ansible_hosts.ini
|
|
||||||
profile/*
|
profile/*
|
||||||
public/resource/javascript/vue.js
|
/gocron
|
||||||
gocron
|
/gocron-node
|
||||||
gocron.exe
|
/bin
|
||||||
gocron-node
|
/web/public/static
|
||||||
gocron-node.exe
|
/web/public/index.html
|
||||||
|
/gocron-package
|
||||||
|
/gocron-node-package
|
||||||
|
|
||||||
node_modules
|
node_modules
|
||||||
package.json
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
language: go
|
language: go
|
||||||
go:
|
go:
|
||||||
- 1.7.x
|
- 1.9.x
|
||||||
script: go test `go list ./... | grep -v vendor`
|
script: go test `go list ./... | grep -v vendor`
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
FROM golang:1.10-alpine as builder
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/ouqiang/gocron
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN apk update \
|
||||||
|
&& apk add --no-cache git ca-certificates make bash nodejs yarn
|
||||||
|
|
||||||
|
RUN make install-vue \
|
||||||
|
&& make build-vue \
|
||||||
|
&& make statik \
|
||||||
|
&& CGO_ENABLED=0 make gocron
|
||||||
|
|
||||||
|
FROM alpine:3.7
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates \
|
||||||
|
&& addgroup -S app \
|
||||||
|
&& adduser -S -g app app
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /go/src/github.com/ouqiang/gocron/bin/gocron .
|
||||||
|
|
||||||
|
RUN chown -R app:app ./
|
||||||
|
|
||||||
|
EXPOSE 5920
|
||||||
|
|
||||||
|
USER app
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/gocron", "web"]
|
||||||
@@ -1,29 +1,33 @@
|
|||||||
[](https://travis-ci.org/ouqiang/gocron)
|
|
||||||
# gocron - 定时任务管理系统
|
# gocron - 定时任务管理系统
|
||||||
|
[](https://travis-ci.org/ouqiang/gocron)
|
||||||
|
[](https://github.com/ouqiang/gocron/releases)
|
||||||
|
[](https://github.com/ouqiang/gocron/blob/master/LICENSE)
|
||||||
|
[](https://github.com/ouqiang/gocron/releases)
|
||||||
|
|
||||||
# 项目简介
|
# 项目简介
|
||||||
使用Go语言开发的定时任务集中调度和管理系统, 用于替代Linux-crontab [查看文档](https://github.com/ouqiang/gocron/wiki)
|
使用Go语言开发的轻量级定时任务集中调度和管理系统, 用于替代Linux-crontab [查看文档](https://github.com/ouqiang/gocron/wiki)
|
||||||
|
|
||||||
原有的延时任务拆分为独立项目[延迟队列](https://github.com/ouqiang/delay-queue)
|
原有的延时任务拆分为独立项目[延迟队列](https://github.com/ouqiang/delay-queue)
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
* Web界面管理定时任务, 支持动态添加、删除任务
|
* Web界面管理定时任务
|
||||||
* crontab时间表达式,精确到秒
|
* crontab时间表达式, 精确到秒
|
||||||
* 任务执行失败重试设置
|
* 任务执行失败可重试
|
||||||
* 任务超时设置
|
* 任务执行超时, 强制结束
|
||||||
* 任务依赖配置
|
* 任务依赖配置, A任务完成后再执行B任务
|
||||||
|
* 账户权限控制
|
||||||
* 任务类型
|
* 任务类型
|
||||||
* shell任务
|
* shell任务
|
||||||
> 在任务节点上执行shell命令, 支持任务同时在多个节点上运行
|
> 在任务节点上执行shell命令, 支持任务同时在多个节点上运行
|
||||||
* HTTP任务
|
* HTTP任务
|
||||||
> 访问指定的URL地址, 由调度器直接执行, 不依赖任务节点
|
> 访问指定的URL地址, 由调度器直接执行, 不依赖任务节点
|
||||||
* 查看任务执行日志
|
* 查看任务执行结果日志
|
||||||
* 任务执行结果通知, 支持邮件、Slack
|
* 任务执行结果通知, 支持邮件、Slack、Webhook
|
||||||
|
|
||||||
### 截图
|
### 截图
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
### 支持平台
|
### 支持平台
|
||||||
> Windows、Linux、Mac OS
|
> Windows、Linux、Mac OS
|
||||||
@@ -33,8 +37,9 @@
|
|||||||
|
|
||||||
|
|
||||||
## 下载
|
## 下载
|
||||||
[v1.1](https://github.com/ouqiang/gocron/releases/tag/v1.1)
|
[releases](https://github.com/ouqiang/gocron/releases)
|
||||||
|
|
||||||
|
[版本升级](https://github.com/ouqiang/gocron/wiki/版本升级)
|
||||||
|
|
||||||
## 安装
|
## 安装
|
||||||
|
|
||||||
@@ -51,30 +56,47 @@
|
|||||||
4. 浏览器访问 http://localhost:5920
|
4. 浏览器访问 http://localhost:5920
|
||||||
|
|
||||||
### 源码安装
|
### 源码安装
|
||||||
1. `go`语言版本1.7+
|
- 安装Go 1.9+
|
||||||
2. `go get -d github.com/ouqiang/gocron`
|
- `go get -d github.com/ouqiang/gocron`
|
||||||
3. 编译
|
- 编译 `make`
|
||||||
* 调度器 `go build -tags gocron -o gocron`
|
- 启动
|
||||||
* 任务节点 `go build -tags node -o gocron-node`
|
* gocron `./bin/gocron web`
|
||||||
4. 启动、访问方式同上
|
* gocron-node `./bin/gocron-node`
|
||||||
|
|
||||||
### 命令
|
### 命令
|
||||||
|
|
||||||
|
* gocron
|
||||||
|
* -v 查看版本
|
||||||
|
|
||||||
* gocron web
|
* gocron web
|
||||||
* --host 默认0.0.0.0
|
* --host 默认0.0.0.0
|
||||||
* -p 端口, 指定端口, 默认5920
|
* -p 端口, 指定端口, 默认5920
|
||||||
* -e 指定运行环境, dev|test|prod, dev模式下可查看更多日志信息, 默认prod
|
* -e 指定运行环境, dev|test|prod, dev模式下可查看更多日志信息, 默认prod
|
||||||
* -h 查看帮助
|
* -h 查看帮助
|
||||||
* gocron-node
|
* gocron-node
|
||||||
* -allow-root *nix平台允许以root用户运行
|
* -allow-root *nix平台允许以root用户运行
|
||||||
* -s ip:port 监听地址
|
* -s ip:port 监听地址
|
||||||
|
* -enable-tls 开启TLS
|
||||||
|
* -ca-file CA证书文件
|
||||||
|
* -cert-file 证书文件
|
||||||
|
* -key-file 私钥文件
|
||||||
|
* -h 查看帮助
|
||||||
|
* -v 查看版本
|
||||||
|
|
||||||
|
## To Do List
|
||||||
|
- [x] 版本升级
|
||||||
|
- [x] 批量开启、关闭、删除任务
|
||||||
|
- [x] 调度器与任务节点通信支持https
|
||||||
|
- [x] 任务分组
|
||||||
|
- [x] 多用户
|
||||||
|
- [x] 权限控制
|
||||||
|
|
||||||
## 程序使用的组件
|
## 程序使用的组件
|
||||||
* Web框架 [Macaron](http://go-macaron.com/)
|
* Web框架 [Macaron](http://go-macaron.com/)
|
||||||
* 定时任务调度 [Cron](https://github.com/robfig/cron)
|
* 定时任务调度 [Cron](https://github.com/robfig/cron)
|
||||||
* ORM [Xorm](https://github.com/go-xorm/xorm)
|
* ORM [Xorm](https://github.com/go-xorm/xorm)
|
||||||
* UI框架 [Semantic UI](https://semantic-ui.com/)
|
* UI框架 [Element UI](https://github.com/ElemeFE/element)
|
||||||
* 依赖管理(所有依赖包放入vendor目录) [Govendor](https://github.com/kardianos/govendor)
|
* 依赖管理 [Govendor](https://github.com/kardianos/govendor)
|
||||||
* RPC框架 [gRPC](https://github.com/grpc/grpc)
|
* RPC框架 [gRPC](https://github.com/grpc/grpc)
|
||||||
|
|
||||||
## 反馈
|
## 反馈
|
||||||
@@ -82,6 +104,36 @@
|
|||||||
|
|
||||||
## ChangeLog
|
## ChangeLog
|
||||||
|
|
||||||
|
v1.5
|
||||||
|
--------
|
||||||
|
* 前端使用Vue+ElementUI重构
|
||||||
|
* 任务通知
|
||||||
|
* 新增WebHook通知
|
||||||
|
* 自定义通知模板
|
||||||
|
* 匹配任务执行结果关键字发送通知
|
||||||
|
* 任务列表页显示任务下次执行时间
|
||||||
|
|
||||||
|
v1.4
|
||||||
|
--------
|
||||||
|
* HTTP任务支持POST请求
|
||||||
|
* 后台手动停止运行中的shell任务
|
||||||
|
* 任务执行失败重试间隔时间支持用户自定义
|
||||||
|
* 修复API接口调用报403错误
|
||||||
|
|
||||||
|
v1.3
|
||||||
|
--------
|
||||||
|
* 支持多用户登录
|
||||||
|
* 增加用户权限控制
|
||||||
|
|
||||||
|
|
||||||
|
v1.2.2
|
||||||
|
--------
|
||||||
|
* 用户登录页增加图形验证码
|
||||||
|
* 支持从旧版本升级
|
||||||
|
* 任务批量开启、关闭、删除
|
||||||
|
* 调度器与任务节点支持HTTPS双向认证
|
||||||
|
* 修复任务列表页总记录数显示错误
|
||||||
|
|
||||||
v1.1
|
v1.1
|
||||||
--------
|
--------
|
||||||
|
|
||||||
|
|||||||
|
After Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 340 KiB |
@@ -1,112 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# set -x -u
|
|
||||||
# 构建应用, 生成压缩包 gocron.zip或gocron.tar.gz
|
|
||||||
# ./build.sh -p windows -a amd64
|
|
||||||
# 参数含义
|
|
||||||
# -p 指定平台(windows|linux|darwin)
|
|
||||||
# -a 指定体系架构(amd64|386), 默认amd64
|
|
||||||
|
|
||||||
|
|
||||||
TEMP_DIR=`date +%s`-temp-`echo $RANDOM`
|
|
||||||
|
|
||||||
# 目标平台 windows,linux,darwin
|
|
||||||
OS=''
|
|
||||||
# 目标平台架构
|
|
||||||
ARCH=''
|
|
||||||
# 应用名称
|
|
||||||
APP_NAME='gocron'
|
|
||||||
# 可执行文件名
|
|
||||||
EXEC_NAME=''
|
|
||||||
# 压缩包名称
|
|
||||||
COMPRESS_FILE=''
|
|
||||||
|
|
||||||
|
|
||||||
# -p 平台 -a 架构
|
|
||||||
while getopts "p:a:" OPT;
|
|
||||||
do
|
|
||||||
case $OPT in
|
|
||||||
p) OS=$OPTARG
|
|
||||||
;;
|
|
||||||
a) ARCH=$OPTARG
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z $OS ]];then
|
|
||||||
echo "平台不能为空"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $OS != 'windows' && $OS != 'linux' && $OS != 'darwin' ]];then
|
|
||||||
echo '平台错误,支持的平台 windows linux darmin(osx)'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z $ARCH ]];then
|
|
||||||
ARCH='amd64'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
|
||||||
echo 'arch错误,仅支持 386 amd64'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo '开始编译调度器'
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
GOOS=$OS GOARCH=$ARCH go build -tags gocron -ldflags '-w'
|
|
||||||
else
|
|
||||||
GOOS=$OS GOARCH=$ARCH go build -tags gocron -ldflags '-w'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $? != 0 ]];then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo '编译完成'
|
|
||||||
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
EXEC_NAME=${APP_NAME}.exe
|
|
||||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.zip
|
|
||||||
else
|
|
||||||
EXEC_NAME=${APP_NAME}
|
|
||||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p $TEMP_DIR/$APP_NAME
|
|
||||||
if [[ $? != 0 ]]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 需要打包的文件
|
|
||||||
PACKAGE_FILENAME=(conf log public data templates ${EXEC_NAME})
|
|
||||||
|
|
||||||
echo '复制文件到临时目录'
|
|
||||||
# 复制文件到临时目录
|
|
||||||
for i in ${PACKAGE_FILENAME[*]}
|
|
||||||
do
|
|
||||||
cp -r $i $TEMP_DIR/$APP_NAME
|
|
||||||
done
|
|
||||||
|
|
||||||
# 删除运行时产生的文件
|
|
||||||
rm -rf $TEMP_DIR/$APP_NAME/conf/*
|
|
||||||
rm -rf $TEMP_DIR/$APP_NAME/log/*
|
|
||||||
rm -rf $TEMP_DIR/$APP_NAME/data/sessions/*
|
|
||||||
rm -rf $TEMP_DIR/$APP_NAME/data/ssh/password/*
|
|
||||||
rm -rf $TEMP_DIR/$APP_NAME/data/ssh/private_key/*
|
|
||||||
|
|
||||||
echo '压缩文件'
|
|
||||||
# 压缩文件
|
|
||||||
cd $TEMP_DIR
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
zip -rq $COMPRESS_FILE *
|
|
||||||
else
|
|
||||||
tar czf $COMPRESS_FILE *
|
|
||||||
fi
|
|
||||||
mv $COMPRESS_FILE ../
|
|
||||||
cd ../
|
|
||||||
|
|
||||||
rm $EXEC_NAME
|
|
||||||
rm -rf $TEMP_DIR
|
|
||||||
|
|
||||||
echo '打包完成'
|
|
||||||
echo '生成压缩文件--' $COMPRESS_FILE
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# set -x -u
|
|
||||||
# 任务节点打包, 生成压缩包 gocron-node.zip或gocron-node.tar.gz
|
|
||||||
# ./build-node.sh -p windows -a amd64
|
|
||||||
# 参数含义
|
|
||||||
# -p 指定平台(windows|linux|darwin)
|
|
||||||
# -a 指定体系架构(amd64|386), 默认amd64
|
|
||||||
|
|
||||||
|
|
||||||
# 目标平台 windows,linux,darwin
|
|
||||||
OS=''
|
|
||||||
# 目标平台架构
|
|
||||||
ARCH=''
|
|
||||||
# 应用名称
|
|
||||||
APP_NAME='gocron-node'
|
|
||||||
# 可执行文件名
|
|
||||||
EXEC_NAME=''
|
|
||||||
# 压缩包名称
|
|
||||||
COMPRESS_FILE=''
|
|
||||||
|
|
||||||
|
|
||||||
# -p 平台 -a 架构
|
|
||||||
while getopts "p:a:" OPT;
|
|
||||||
do
|
|
||||||
case $OPT in
|
|
||||||
p) OS=$OPTARG
|
|
||||||
;;
|
|
||||||
a) ARCH=$OPTARG
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
if [[ -z $OS ]];then
|
|
||||||
echo "平台不能为空"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $OS != 'windows' && $OS != 'linux' && $OS != 'darwin' ]];then
|
|
||||||
echo '平台错误,支持的平台 windows linux darmin(osx)'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z $ARCH ]];then
|
|
||||||
ARCH='amd64'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $ARCH != '386' && $ARCH != 'amd64' ]];then
|
|
||||||
echo 'arch错误,仅支持 386 amd64'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
EXEC_NAME=${APP_NAME}.exe
|
|
||||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.zip
|
|
||||||
else
|
|
||||||
EXEC_NAME=${APP_NAME}
|
|
||||||
COMPRESS_FILE=${APP_NAME}-${OS}-${ARCH}.tar.gz
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo '开始编译任务节点'
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
GOOS=$OS GOARCH=$ARCH go build -tags node -ldflags '-w' -o $EXEC_NAME
|
|
||||||
else
|
|
||||||
GOOS=$OS GOARCH=$ARCH go build -tags node -ldflags '-w' -o $EXEC_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $? != 0 ]];then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo '编译完成'
|
|
||||||
|
|
||||||
if [[ $OS = 'windows' ]];then
|
|
||||||
zip -rq $COMPRESS_FILE $EXEC_NAME
|
|
||||||
else
|
|
||||||
tar czf $COMPRESS_FILE $EXEC_NAME
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
rm $EXEC_NAME
|
|
||||||
|
|
||||||
echo '打包完成'
|
|
||||||
echo '生成压缩文件--' $COMPRESS_FILE
|
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
// Command gocron
|
||||||
|
//go:generate statik -src=../../web/public -dest=../../internal -f
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers"
|
||||||
|
"github.com/ouqiang/gocron/internal/service"
|
||||||
|
"github.com/ouqiang/goutil"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AppVersion = "1.5"
|
||||||
|
BuildDate, GitCommit string
|
||||||
|
)
|
||||||
|
|
||||||
|
// web服务器默认端口
|
||||||
|
const DefaultPort = 5920
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cliApp := cli.NewApp()
|
||||||
|
cliApp.Name = "gocron"
|
||||||
|
cliApp.Usage = "gocron service"
|
||||||
|
cliApp.Version, _ = goutil.FormatAppVersion(AppVersion, GitCommit, BuildDate)
|
||||||
|
cliApp.Commands = getCommands()
|
||||||
|
cliApp.Flags = append(cliApp.Flags, []cli.Flag{}...)
|
||||||
|
cliApp.Run(os.Args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCommands
|
||||||
|
func getCommands() []cli.Command {
|
||||||
|
command := cli.Command{
|
||||||
|
Name: "web",
|
||||||
|
Usage: "run web server",
|
||||||
|
Action: runWeb,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "host",
|
||||||
|
Value: "0.0.0.0",
|
||||||
|
Usage: "bind host",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "port,p",
|
||||||
|
Value: DefaultPort,
|
||||||
|
Usage: "bind port",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "env,e",
|
||||||
|
Value: "prod",
|
||||||
|
Usage: "runtime environment, dev|test|prod",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return []cli.Command{command}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runWeb(ctx *cli.Context) {
|
||||||
|
// 设置运行环境
|
||||||
|
setEnvironment(ctx)
|
||||||
|
// 初始化应用
|
||||||
|
app.InitEnv(AppVersion)
|
||||||
|
// 初始化模块 DB、定时任务等
|
||||||
|
initModule()
|
||||||
|
// 捕捉信号,配置热更新等
|
||||||
|
go catchSignal()
|
||||||
|
m := macaron.Classic()
|
||||||
|
// 注册路由
|
||||||
|
routers.Register(m)
|
||||||
|
// 注册中间件.
|
||||||
|
routers.RegisterMiddleware(m)
|
||||||
|
host := parseHost(ctx)
|
||||||
|
port := parsePort(ctx)
|
||||||
|
m.Run(host, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initModule() {
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := setting.Read(app.AppConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("读取应用配置失败", err)
|
||||||
|
}
|
||||||
|
app.Setting = config
|
||||||
|
|
||||||
|
// 初始化DB
|
||||||
|
models.Db = models.CreateDb()
|
||||||
|
|
||||||
|
// 版本升级
|
||||||
|
upgradeIfNeed()
|
||||||
|
|
||||||
|
// 初始化定时任务
|
||||||
|
service.ServiceTask.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析端口
|
||||||
|
func parsePort(ctx *cli.Context) int {
|
||||||
|
port := DefaultPort
|
||||||
|
if ctx.IsSet("port") {
|
||||||
|
port = ctx.Int("port")
|
||||||
|
}
|
||||||
|
if port <= 0 || port >= 65535 {
|
||||||
|
port = DefaultPort
|
||||||
|
}
|
||||||
|
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseHost(ctx *cli.Context) string {
|
||||||
|
if ctx.IsSet("host") {
|
||||||
|
return ctx.String("host")
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0.0.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func setEnvironment(ctx *cli.Context) {
|
||||||
|
env := "prod"
|
||||||
|
if ctx.IsSet("env") {
|
||||||
|
env = ctx.String("env")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch env {
|
||||||
|
case "test":
|
||||||
|
macaron.Env = macaron.TEST
|
||||||
|
case "dev":
|
||||||
|
macaron.Env = macaron.DEV
|
||||||
|
default:
|
||||||
|
macaron.Env = macaron.PROD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 捕捉信号
|
||||||
|
func catchSignal() {
|
||||||
|
c := make(chan os.Signal)
|
||||||
|
// todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2
|
||||||
|
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
for {
|
||||||
|
s := <-c
|
||||||
|
logger.Info("收到信号 -- ", s)
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGHUP:
|
||||||
|
logger.Info("收到终端断开信号, 忽略")
|
||||||
|
case syscall.SIGINT, syscall.SIGTERM:
|
||||||
|
shutdown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用退出
|
||||||
|
func shutdown() {
|
||||||
|
defer func() {
|
||||||
|
logger.Info("已退出")
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("应用准备退出")
|
||||||
|
// 停止所有任务调度
|
||||||
|
logger.Info("停止定时任务调度")
|
||||||
|
service.ServiceTask.WaitAndExit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断应用是否需要升级, 当存在版本号文件且版本小于app.VersionId时升级
|
||||||
|
func upgradeIfNeed() {
|
||||||
|
currentVersionId := app.GetCurrentVersionId()
|
||||||
|
// 没有版本号文件
|
||||||
|
if currentVersionId == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if currentVersionId >= app.VersionId {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
migration := new(models.Migration)
|
||||||
|
logger.Infof("版本升级开始, 当前版本号%d", currentVersionId)
|
||||||
|
|
||||||
|
migration.Upgrade(currentVersionId)
|
||||||
|
app.UpdateVersionFile()
|
||||||
|
|
||||||
|
logger.Infof("已升级到最新版本%d", app.VersionId)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
// Command gocron-node
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/server"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/goutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
AppVersion, BuildDate, GitCommit string
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var serverAddr string
|
||||||
|
var allowRoot bool
|
||||||
|
var version bool
|
||||||
|
var CAFile string
|
||||||
|
var certFile string
|
||||||
|
var keyFile string
|
||||||
|
var enableTLS bool
|
||||||
|
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
||||||
|
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
||||||
|
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
||||||
|
flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls")
|
||||||
|
flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path")
|
||||||
|
flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path")
|
||||||
|
flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if version {
|
||||||
|
goutil.PrintAppVersion(AppVersion, GitCommit, BuildDate)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableTLS {
|
||||||
|
if !utils.FileExist(CAFile) {
|
||||||
|
fmt.Printf("failed to read ca cert file: %s", CAFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !utils.FileExist(certFile) {
|
||||||
|
fmt.Printf("failed to read server cert file: %s", certFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !utils.FileExist(keyFile) {
|
||||||
|
fmt.Printf("failed to read server key file: %s", keyFile)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
certificate := auth.Certificate{
|
||||||
|
CAFile: strings.TrimSpace(CAFile),
|
||||||
|
CertFile: strings.TrimSpace(certFile),
|
||||||
|
KeyFile: strings.TrimSpace(keyFile),
|
||||||
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
||||||
|
fmt.Println("Do not run gocron-node as root user")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
server.Start(serverAddr, enableTLS, certificate)
|
||||||
|
}
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
package cmd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ouqiang/gocron/modules/app"
|
|
||||||
"github.com/ouqiang/gocron/routers"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
"gopkg.in/macaron.v1"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"github.com/ouqiang/gocron/service"
|
|
||||||
"github.com/ouqiang/gocron/models"
|
|
||||||
"github.com/ouqiang/gocron/modules/setting"
|
|
||||||
"time"
|
|
||||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
|
||||||
)
|
|
||||||
|
|
||||||
// web服务器默认端口
|
|
||||||
const DefaultPort = 5920
|
|
||||||
|
|
||||||
|
|
||||||
var CmdWeb = cli.Command{
|
|
||||||
Name: "web",
|
|
||||||
Usage: "run web server",
|
|
||||||
Action: runWeb,
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "host",
|
|
||||||
Value: "0.0.0.0",
|
|
||||||
Usage: "bind host",
|
|
||||||
},
|
|
||||||
cli.IntFlag{
|
|
||||||
Name: "port,p",
|
|
||||||
Value: DefaultPort,
|
|
||||||
Usage: "bind port",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "env,e",
|
|
||||||
Value: "prod",
|
|
||||||
Usage: "runtime environment, dev|test|prod",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func runWeb(ctx *cli.Context) {
|
|
||||||
// 设置运行环境
|
|
||||||
setEnvironment(ctx)
|
|
||||||
// 初始化应用
|
|
||||||
app.InitEnv()
|
|
||||||
// 初始化模块 DB、定时任务等
|
|
||||||
initModule()
|
|
||||||
// 捕捉信号,配置热更新等
|
|
||||||
go catchSignal()
|
|
||||||
m := macaron.Classic()
|
|
||||||
|
|
||||||
// 注册路由
|
|
||||||
routers.Register(m)
|
|
||||||
// 注册中间件.
|
|
||||||
routers.RegisterMiddleware(m)
|
|
||||||
host := parseHost(ctx)
|
|
||||||
port := parsePort(ctx)
|
|
||||||
m.Run(host, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
func initModule() {
|
|
||||||
if !app.Installed {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config, err := setting.Read(app.AppConfig)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("读取应用配置失败", err)
|
|
||||||
}
|
|
||||||
app.Setting = config
|
|
||||||
|
|
||||||
models.Db = models.CreateDb()
|
|
||||||
|
|
||||||
// 初始化定时任务
|
|
||||||
serviceTask := new(service.Task)
|
|
||||||
serviceTask.Initialize()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析端口
|
|
||||||
func parsePort(ctx *cli.Context) int {
|
|
||||||
var port int = DefaultPort
|
|
||||||
if ctx.IsSet("port") {
|
|
||||||
port = ctx.Int("port")
|
|
||||||
}
|
|
||||||
if port <= 0 || port >= 65535 {
|
|
||||||
port = DefaultPort
|
|
||||||
}
|
|
||||||
|
|
||||||
return port
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseHost(ctx *cli.Context) string {
|
|
||||||
if ctx.IsSet("host") {
|
|
||||||
return ctx.String("host")
|
|
||||||
}
|
|
||||||
|
|
||||||
return "0.0.0.0"
|
|
||||||
}
|
|
||||||
|
|
||||||
func setEnvironment(ctx *cli.Context) {
|
|
||||||
var env string = "prod"
|
|
||||||
if ctx.IsSet("env") {
|
|
||||||
env = ctx.String("env")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch env {
|
|
||||||
case "test":
|
|
||||||
macaron.Env = macaron.TEST
|
|
||||||
case "dev":
|
|
||||||
macaron.Env = macaron.DEV
|
|
||||||
default:
|
|
||||||
macaron.Env = macaron.PROD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 捕捉信号
|
|
||||||
func catchSignal() {
|
|
||||||
c := make(chan os.Signal)
|
|
||||||
// todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2
|
|
||||||
signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
for {
|
|
||||||
s := <- c
|
|
||||||
logger.Info("收到信号 -- ", s)
|
|
||||||
switch s {
|
|
||||||
case syscall.SIGHUP:
|
|
||||||
logger.Info("收到终端断开信号, 忽略")
|
|
||||||
case syscall.SIGINT, syscall.SIGTERM:
|
|
||||||
shutdown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 应用退出
|
|
||||||
func shutdown() {
|
|
||||||
defer func() {
|
|
||||||
logger.Info("已退出")
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
if !app.Installed {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Info("应用准备退出")
|
|
||||||
serviceTask := new(service.Task)
|
|
||||||
// 停止所有任务调度
|
|
||||||
logger.Info("停止定时任务调度")
|
|
||||||
serviceTask.StopAll()
|
|
||||||
|
|
||||||
taskNumInRunning := service.TaskNum.Num()
|
|
||||||
logger.Infof("正在运行的任务有%d个", taskNumInRunning)
|
|
||||||
if taskNumInRunning > 0 {
|
|
||||||
logger.Info("等待所有任务执行完成后退出")
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
if taskNumInRunning <= 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(3 * time.Second)
|
|
||||||
taskNumInRunning = service.TaskNum.Num()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 释放gRPC连接池
|
|
||||||
grpcpool.Pool.ReleaseAll()
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// +build node
|
|
||||||
// 任务节点
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ouqiang/gocron/modules/rpc/server"
|
|
||||||
"flag"
|
|
||||||
"runtime"
|
|
||||||
"os"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
const AppVersion = "1.1"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var serverAddr string
|
|
||||||
var allowRoot bool
|
|
||||||
var version bool
|
|
||||||
flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root")
|
|
||||||
flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port")
|
|
||||||
flag.BoolVar(&version, "v", false, "./gocron-node -v")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if version {
|
|
||||||
fmt.Println(AppVersion)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot {
|
|
||||||
fmt.Println("Do not run gocron-node as root user")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
server.Start(serverAddr)
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
// +build gocron
|
|
||||||
// 调度中心
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ouqiang/gocron/cmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
const AppVersion = "1.1"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "gocron"
|
|
||||||
app.Usage = "gocron service"
|
|
||||||
app.Version = AppVersion
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
cmd.CmdWeb,
|
|
||||||
}
|
|
||||||
app.Flags = append(app.Flags, []cli.Flag{}...)
|
|
||||||
app.Run(os.Args)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 主机
|
||||||
|
type Host struct {
|
||||||
|
Id int16 `json:"id" xorm:"smallint pk autoincr"`
|
||||||
|
Name string `json:"name" xorm:"varchar(64) notnull"` // 主机名称
|
||||||
|
Alias string `json:"alias" xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||||
|
Port int `json:"port" xorm:"notnull default 22"` // 主机端口
|
||||||
|
Remark string `json:"remark" xorm:"varchar(100) notnull default '' "` // 备注
|
||||||
|
BaseModel `json:"-" xorm:"-"`
|
||||||
|
Selected bool `json:"-" xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
func (host *Host) Create() (insertId int16, err error) {
|
||||||
|
_, err = Db.Insert(host)
|
||||||
|
if err == nil {
|
||||||
|
insertId = host.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) UpdateBean(id int16) (int64, error) {
|
||||||
|
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
func (host *Host) Update(id int, data CommonMap) (int64, error) {
|
||||||
|
return Db.Table(host).ID(id).Update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
func (host *Host) Delete(id int) (int64, error) {
|
||||||
|
return Db.Id(id).Delete(new(Host))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) Find(id int) error {
|
||||||
|
_, err := Db.Id(id).Get(host)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) NameExists(name string, id int16) (bool, error) {
|
||||||
|
if id == 0 {
|
||||||
|
count, err := Db.Where("name = ?", name).Count(host)
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host)
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) List(params CommonMap) ([]Host, error) {
|
||||||
|
host.parsePageAndPageSize(params)
|
||||||
|
list := make([]Host, 0)
|
||||||
|
session := Db.Desc("id")
|
||||||
|
host.parseWhere(session, params)
|
||||||
|
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) AllList() ([]Host, error) {
|
||||||
|
list := make([]Host, 0)
|
||||||
|
err := Db.Cols("name,port").Desc("id").Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (host *Host) Total(params CommonMap) (int64, error) {
|
||||||
|
session := Db.NewSession()
|
||||||
|
host.parseWhere(session, params)
|
||||||
|
return session.Count(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析where
|
||||||
|
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, ok := params["Id"]
|
||||||
|
if ok && id.(int) > 0 {
|
||||||
|
session.And("id = ?", id)
|
||||||
|
}
|
||||||
|
name, ok := params["Name"]
|
||||||
|
if ok && name.(string) != "" {
|
||||||
|
session.And("name = ?", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 用户登录日志
|
||||||
|
|
||||||
|
type LoginLog struct {
|
||||||
|
Id int `json:"id" xorm:"pk autoincr notnull "`
|
||||||
|
Username string `json:"username" xorm:"varchar(32) notnull"`
|
||||||
|
Ip string `json:"ip" xorm:"varchar(15) not null"`
|
||||||
|
Created time.Time `json:"created" xorm:"datetime notnull created"`
|
||||||
|
BaseModel `json:"-" xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *LoginLog) Create() (insertId int, err error) {
|
||||||
|
_, err = Db.Insert(log)
|
||||||
|
if err == nil {
|
||||||
|
insertId = log.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) {
|
||||||
|
log.parsePageAndPageSize(params)
|
||||||
|
list := make([]LoginLog, 0)
|
||||||
|
err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (log *LoginLog) Total() (int64, error) {
|
||||||
|
return Db.Count(log)
|
||||||
|
}
|
||||||
@@ -0,0 +1,247 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Migration struct{}
|
||||||
|
|
||||||
|
// 首次安装, 创建数据库表
|
||||||
|
func (migration *Migration) Install(dbName string) error {
|
||||||
|
if !isDatabaseExist(dbName) {
|
||||||
|
return errors.New("数据库不存在")
|
||||||
|
}
|
||||||
|
setting := new(Setting)
|
||||||
|
task := new(Task)
|
||||||
|
tables := []interface{}{
|
||||||
|
&User{}, task, &TaskLog{}, &Host{}, setting, &LoginLog{}, &TaskHost{},
|
||||||
|
}
|
||||||
|
for _, table := range tables {
|
||||||
|
exist, err := Db.IsTableExist(table)
|
||||||
|
if exist {
|
||||||
|
return errors.New("数据表已存在")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = Db.Sync2(table)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setting.InitBasicField()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断数据库是否存在
|
||||||
|
func isDatabaseExist(name string) bool {
|
||||||
|
_, err := Db.Exec("use ?", name)
|
||||||
|
|
||||||
|
return err != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 迭代升级数据库, 新建表、新增字段等
|
||||||
|
func (migration *Migration) Upgrade(oldVersionId int) {
|
||||||
|
// v1.2版本不支持升级
|
||||||
|
if oldVersionId == 120 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
versionIds := []int{110, 122, 130, 140, 150}
|
||||||
|
upgradeFuncs := []func(*xorm.Session) error{
|
||||||
|
migration.upgradeFor110,
|
||||||
|
migration.upgradeFor122,
|
||||||
|
migration.upgradeFor130,
|
||||||
|
migration.upgradeFor140,
|
||||||
|
migration.upgradeFor150,
|
||||||
|
}
|
||||||
|
|
||||||
|
startIndex := -1
|
||||||
|
// 从当前版本的下一版本开始升级
|
||||||
|
for i, value := range versionIds {
|
||||||
|
if value > oldVersionId {
|
||||||
|
startIndex = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if startIndex == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(versionIds)
|
||||||
|
if startIndex >= length {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session := Db.NewSession()
|
||||||
|
err := session.Begin()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("开启事务失败-%s", err.Error())
|
||||||
|
}
|
||||||
|
for startIndex < length {
|
||||||
|
err = upgradeFuncs[startIndex](session)
|
||||||
|
if err == nil {
|
||||||
|
startIndex++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
dbErr := session.Rollback()
|
||||||
|
if dbErr != nil {
|
||||||
|
logger.Fatalf("事务回滚失败-%s", dbErr.Error())
|
||||||
|
}
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
err = session.Commit()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("提交事务失败-%s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级到v1.1版本
|
||||||
|
func (migration *Migration) upgradeFor110(session *xorm.Session) error {
|
||||||
|
logger.Info("开始升级到v1.1")
|
||||||
|
// 创建表task_host
|
||||||
|
err := session.Sync2(new(TaskHost))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tableName := TablePrefix + "task"
|
||||||
|
// 把task对应的host_id写入task_host表
|
||||||
|
sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName)
|
||||||
|
results, err := session.Query(sql)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range results {
|
||||||
|
taskHostModel := &TaskHost{}
|
||||||
|
taskId, err := strconv.Atoi(string(value["id"]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
hostId, err := strconv.Atoi(string(value["host_id"]))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
taskHostModel.TaskId = taskId
|
||||||
|
taskHostModel.HostId = int16(hostId)
|
||||||
|
_, err = session.Insert(taskHostModel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除task表host_id字段
|
||||||
|
_, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName))
|
||||||
|
|
||||||
|
logger.Info("已升级到v1.1\n")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级到1.2.2版本
|
||||||
|
func (migration *Migration) upgradeFor122(session *xorm.Session) error {
|
||||||
|
logger.Info("开始升级到v1.2.2")
|
||||||
|
|
||||||
|
tableName := TablePrefix + "task"
|
||||||
|
// task表增加tag字段
|
||||||
|
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName))
|
||||||
|
|
||||||
|
logger.Info("已升级到v1.2.2\n")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级到v1.3版本
|
||||||
|
func (migration *Migration) upgradeFor130(session *xorm.Session) error {
|
||||||
|
logger.Info("开始升级到v1.3")
|
||||||
|
|
||||||
|
tableName := TablePrefix + "user"
|
||||||
|
// 删除user表deleted字段
|
||||||
|
_, err := session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN deleted", tableName))
|
||||||
|
|
||||||
|
logger.Info("已升级到v1.3\n")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 升级到v1.4版本
|
||||||
|
func (migration *Migration) upgradeFor140(session *xorm.Session) error {
|
||||||
|
logger.Info("开始升级到v1.4")
|
||||||
|
|
||||||
|
tableName := TablePrefix + "task"
|
||||||
|
// task表增加字段
|
||||||
|
// retry_interval 重试间隔时间(秒)
|
||||||
|
// http_method http请求方法
|
||||||
|
sql := fmt.Sprintf(
|
||||||
|
"ALTER TABLE %s ADD COLUMN retry_interval SMALLINT NOT NULL DEFAULT 0,ADD COLUMN http_method TINYINT NOT NULL DEFAULT 1", tableName)
|
||||||
|
_, err := session.Exec(sql)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("已升级到v1.4\n")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Migration) upgradeFor150(session *xorm.Session) error {
|
||||||
|
logger.Info("开始升级到v1.5")
|
||||||
|
|
||||||
|
tableName := TablePrefix + "task"
|
||||||
|
// task表增加字段 notify_keyword
|
||||||
|
sql := fmt.Sprintf(
|
||||||
|
"ALTER TABLE %s ADD COLUMN notify_keyword VARCHAR(128) NOT NULL DEFAULT '' ", tableName)
|
||||||
|
_, err := session.Exec(sql)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settingModel := new(Setting)
|
||||||
|
settingModel.Code = MailCode
|
||||||
|
settingModel.Key = MailTemplateKey
|
||||||
|
settingModel.Value = emailTemplate
|
||||||
|
_, err = Db.Insert(settingModel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
settingModel.Id = 0
|
||||||
|
settingModel.Code = SlackCode
|
||||||
|
settingModel.Key = SlackTemplateKey
|
||||||
|
settingModel.Value = slackTemplate
|
||||||
|
_, err = Db.Insert(settingModel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settingModel.Id = 0
|
||||||
|
settingModel.Code = WebhookCode
|
||||||
|
settingModel.Key = WebhookUrlKey
|
||||||
|
settingModel.Value = ""
|
||||||
|
_, err = Db.Insert(settingModel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
settingModel.Id = 0
|
||||||
|
settingModel.Code = WebhookCode
|
||||||
|
settingModel.Key = WebhookTemplateKey
|
||||||
|
settingModel.Value = webhookTemplate
|
||||||
|
_, err = Db.Insert(settingModel)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("已升级到v1.5\n")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"github.com/go-xorm/core"
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Status int8
|
||||||
|
type CommonMap map[string]interface{}
|
||||||
|
|
||||||
|
var TablePrefix = ""
|
||||||
|
var Db *xorm.Engine
|
||||||
|
|
||||||
|
const (
|
||||||
|
Disabled Status = 0 // 禁用
|
||||||
|
Failure Status = 0 // 失败
|
||||||
|
Enabled Status = 1 // 启用
|
||||||
|
Running Status = 1 // 运行中
|
||||||
|
Finish Status = 2 // 完成
|
||||||
|
Cancel Status = 3 // 取消
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Page = 1 // 当前页数
|
||||||
|
PageSize = 20 // 每页多少条数据
|
||||||
|
MaxPageSize = 100000 // 每次最多取多少条
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultTimeFormat = "2006-01-02 15:04:05"
|
||||||
|
|
||||||
|
type BaseModel struct {
|
||||||
|
Page int `xorm:"-"`
|
||||||
|
PageSize int `xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (model *BaseModel) parsePageAndPageSize(params CommonMap) {
|
||||||
|
page, ok := params["Page"]
|
||||||
|
if ok {
|
||||||
|
model.Page = page.(int)
|
||||||
|
}
|
||||||
|
pageSize, ok := params["PageSize"]
|
||||||
|
if ok {
|
||||||
|
model.PageSize = pageSize.(int)
|
||||||
|
}
|
||||||
|
if model.Page <= 0 {
|
||||||
|
model.Page = Page
|
||||||
|
}
|
||||||
|
if model.PageSize <= 0 {
|
||||||
|
model.PageSize = MaxPageSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (model *BaseModel) pageLimitOffset() int {
|
||||||
|
return (model.Page - 1) * model.PageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建Db
|
||||||
|
func CreateDb() *xorm.Engine {
|
||||||
|
dsn := getDbEngineDSN(app.Setting)
|
||||||
|
engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("创建xorm引擎失败", err)
|
||||||
|
}
|
||||||
|
engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns)
|
||||||
|
engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns)
|
||||||
|
|
||||||
|
if app.Setting.Db.Prefix != "" {
|
||||||
|
// 设置表前缀
|
||||||
|
TablePrefix = app.Setting.Db.Prefix
|
||||||
|
mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix)
|
||||||
|
engine.SetTableMapper(mapper)
|
||||||
|
}
|
||||||
|
// 本地环境开启日志
|
||||||
|
if macaron.Env == macaron.DEV {
|
||||||
|
engine.ShowSQL(true)
|
||||||
|
engine.Logger().SetLevel(core.LOG_DEBUG)
|
||||||
|
}
|
||||||
|
|
||||||
|
go keepDbAlived(engine)
|
||||||
|
|
||||||
|
return engine
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建临时数据库连接
|
||||||
|
func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) {
|
||||||
|
dsn := getDbEngineDSN(setting)
|
||||||
|
|
||||||
|
return xorm.NewEngine(setting.Db.Engine, dsn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取数据库引擎DSN mysql,sqlite
|
||||||
|
func getDbEngineDSN(setting *setting.Setting) string {
|
||||||
|
engine := strings.ToLower(setting.Db.Engine)
|
||||||
|
dsn := ""
|
||||||
|
switch engine {
|
||||||
|
case "mysql":
|
||||||
|
dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
|
||||||
|
setting.Db.User,
|
||||||
|
setting.Db.Password,
|
||||||
|
setting.Db.Host,
|
||||||
|
setting.Db.Port,
|
||||||
|
setting.Db.Database,
|
||||||
|
setting.Db.Charset)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dsn
|
||||||
|
}
|
||||||
|
|
||||||
|
func keepDbAlived(engine *xorm.Engine) {
|
||||||
|
t := time.Tick(180 * time.Second)
|
||||||
|
for {
|
||||||
|
<-t
|
||||||
|
engine.Ping()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Id int `xorm:"int pk autoincr"`
|
||||||
|
Code string `xorm:"varchar(32) notnull"`
|
||||||
|
Key string `xorm:"varchar(64) notnull"`
|
||||||
|
Value string `xorm:"varchar(4096) notnull default '' "`
|
||||||
|
}
|
||||||
|
|
||||||
|
const slackTemplate = `
|
||||||
|
任务ID: {{.TaskId}}
|
||||||
|
任务名称: {{.TaskName}}
|
||||||
|
状态: {{.Status}}
|
||||||
|
执行结果: {{.Result}}
|
||||||
|
`
|
||||||
|
const emailTemplate = `
|
||||||
|
任务ID: {{.TaskId}}
|
||||||
|
任务名称: {{.TaskName}}
|
||||||
|
状态: {{.Status}}
|
||||||
|
执行结果: {{.Result}}
|
||||||
|
`
|
||||||
|
const webhookTemplate = `
|
||||||
|
{
|
||||||
|
"task_id": "{{.TaskId}}",
|
||||||
|
"task_name": "{{.TaskName}}",
|
||||||
|
"status": "{{.Status}}",
|
||||||
|
"result": "{{.Result}}"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const (
|
||||||
|
SlackCode = "slack"
|
||||||
|
SlackUrlKey = "url"
|
||||||
|
SlackTemplateKey = "template"
|
||||||
|
SlackChannelKey = "channel"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MailCode = "mail"
|
||||||
|
MailTemplateKey = "template"
|
||||||
|
MailServerKey = "server"
|
||||||
|
MailUserKey = "user"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
WebhookCode = "webhook"
|
||||||
|
WebhookTemplateKey = "template"
|
||||||
|
WebhookUrlKey = "url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 初始化基本字段 邮件、slack等
|
||||||
|
func (setting *Setting) InitBasicField() {
|
||||||
|
setting.Code = SlackCode
|
||||||
|
setting.Key = SlackUrlKey
|
||||||
|
setting.Value = ""
|
||||||
|
Db.Insert(setting)
|
||||||
|
setting.Id = 0
|
||||||
|
|
||||||
|
setting.Code = SlackCode
|
||||||
|
setting.Key = SlackTemplateKey
|
||||||
|
setting.Value = slackTemplate
|
||||||
|
Db.Insert(setting)
|
||||||
|
setting.Id = 0
|
||||||
|
|
||||||
|
setting.Code = MailCode
|
||||||
|
setting.Key = MailServerKey
|
||||||
|
setting.Value = ""
|
||||||
|
Db.Insert(setting)
|
||||||
|
setting.Id = 0
|
||||||
|
|
||||||
|
setting.Code = MailCode
|
||||||
|
setting.Key = MailTemplateKey
|
||||||
|
setting.Value = emailTemplate
|
||||||
|
Db.Insert(setting)
|
||||||
|
setting.Id = 0
|
||||||
|
|
||||||
|
setting.Code = WebhookCode
|
||||||
|
setting.Key = WebhookTemplateKey
|
||||||
|
setting.Value = webhookTemplate
|
||||||
|
Db.Insert(setting)
|
||||||
|
setting.Id = 0
|
||||||
|
|
||||||
|
setting.Code = WebhookCode
|
||||||
|
setting.Key = WebhookUrlKey
|
||||||
|
setting.Value = ""
|
||||||
|
Db.Insert(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region slack配置
|
||||||
|
|
||||||
|
type Slack struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Channels []Channel `json:"channels"`
|
||||||
|
Template string `json:"template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Channel struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) Slack() (Slack, error) {
|
||||||
|
list := make([]Setting, 0)
|
||||||
|
err := Db.Where("code = ?", SlackCode).Find(&list)
|
||||||
|
slack := Slack{}
|
||||||
|
if err != nil {
|
||||||
|
return slack, err
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.formatSlack(list, &slack)
|
||||||
|
|
||||||
|
return slack, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) formatSlack(list []Setting, slack *Slack) {
|
||||||
|
for _, v := range list {
|
||||||
|
switch v.Key {
|
||||||
|
case SlackUrlKey:
|
||||||
|
slack.Url = v.Value
|
||||||
|
case SlackTemplateKey:
|
||||||
|
slack.Template = v.Value
|
||||||
|
default:
|
||||||
|
slack.Channels = append(slack.Channels, Channel{
|
||||||
|
v.Id, v.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) UpdateSlack(url, template string) error {
|
||||||
|
setting.Value = url
|
||||||
|
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: SlackCode, Key: SlackUrlKey})
|
||||||
|
|
||||||
|
setting.Value = template
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: SlackCode, Key: SlackTemplateKey})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建slack渠道
|
||||||
|
func (setting *Setting) CreateChannel(channel string) (int64, error) {
|
||||||
|
setting.Code = SlackCode
|
||||||
|
setting.Key = SlackChannelKey
|
||||||
|
setting.Value = channel
|
||||||
|
|
||||||
|
return Db.Insert(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) IsChannelExist(channel string) bool {
|
||||||
|
setting.Code = SlackCode
|
||||||
|
setting.Key = SlackChannelKey
|
||||||
|
setting.Value = channel
|
||||||
|
|
||||||
|
count, _ := Db.Count(setting)
|
||||||
|
|
||||||
|
return count > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除slack渠道
|
||||||
|
func (setting *Setting) RemoveChannel(id int) (int64, error) {
|
||||||
|
setting.Code = SlackCode
|
||||||
|
setting.Key = SlackChannelKey
|
||||||
|
setting.Id = id
|
||||||
|
return Db.Delete(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
type Mail struct {
|
||||||
|
Host string `json:"host"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
User string `json:"user"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
MailUsers []MailUser `json:"mail_users"`
|
||||||
|
Template string `json:"template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MailUser struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// region 邮件配置
|
||||||
|
func (setting *Setting) Mail() (Mail, error) {
|
||||||
|
list := make([]Setting, 0)
|
||||||
|
err := Db.Where("code = ?", MailCode).Find(&list)
|
||||||
|
mail := Mail{MailUsers: make([]MailUser, 0)}
|
||||||
|
if err != nil {
|
||||||
|
return mail, err
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.formatMail(list, &mail)
|
||||||
|
|
||||||
|
return mail, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) formatMail(list []Setting, mail *Mail) {
|
||||||
|
mailUser := MailUser{}
|
||||||
|
for _, v := range list {
|
||||||
|
switch v.Key {
|
||||||
|
case MailServerKey:
|
||||||
|
json.Unmarshal([]byte(v.Value), mail)
|
||||||
|
case MailUserKey:
|
||||||
|
json.Unmarshal([]byte(v.Value), &mailUser)
|
||||||
|
mailUser.Id = v.Id
|
||||||
|
mail.MailUsers = append(mail.MailUsers, mailUser)
|
||||||
|
case MailTemplateKey:
|
||||||
|
mail.Template = v.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) UpdateMail(config, template string) error {
|
||||||
|
setting.Value = config
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: MailCode, Key: MailServerKey})
|
||||||
|
|
||||||
|
setting.Value = template
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: MailCode, Key: MailTemplateKey})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) CreateMailUser(username, email string) (int64, error) {
|
||||||
|
setting.Code = MailCode
|
||||||
|
setting.Key = MailUserKey
|
||||||
|
mailUser := MailUser{0, username, email}
|
||||||
|
jsonByte, err := json.Marshal(mailUser)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
setting.Value = string(jsonByte)
|
||||||
|
|
||||||
|
return Db.Insert(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) RemoveMailUser(id int) (int64, error) {
|
||||||
|
setting.Code = MailCode
|
||||||
|
setting.Key = MailUserKey
|
||||||
|
setting.Id = id
|
||||||
|
return Db.Delete(setting)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebHook struct {
|
||||||
|
Url string `json:"url"`
|
||||||
|
Template string `json:"template"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) Webhook() (WebHook, error) {
|
||||||
|
list := make([]Setting, 0)
|
||||||
|
err := Db.Where("code = ?", WebhookCode).Find(&list)
|
||||||
|
webHook := WebHook{}
|
||||||
|
if err != nil {
|
||||||
|
return webHook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.formatWebhook(list, &webHook)
|
||||||
|
|
||||||
|
return webHook, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) formatWebhook(list []Setting, webHook *WebHook) {
|
||||||
|
for _, v := range list {
|
||||||
|
switch v.Key {
|
||||||
|
case WebhookUrlKey:
|
||||||
|
webHook.Url = v.Value
|
||||||
|
case WebhookTemplateKey:
|
||||||
|
webHook.Template = v.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (setting *Setting) UpdateWebHook(url, template string) error {
|
||||||
|
setting.Value = url
|
||||||
|
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: WebhookCode, Key: WebhookUrlKey})
|
||||||
|
|
||||||
|
setting.Value = template
|
||||||
|
Db.Cols("value").Update(setting, Setting{Code: WebhookCode, Key: WebhookTemplateKey})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
@@ -0,0 +1,273 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskProtocol int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
||||||
|
TaskRPC // RPC方式执行命令
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskLevel int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskLevelParent TaskLevel = 1 // 父任务
|
||||||
|
TaskLevelChild TaskLevel = 2 // 子任务(依赖任务)
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskDependencyStatus int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖
|
||||||
|
TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskHTTPMethod int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TaskHTTPMethodGet TaskHTTPMethod = 1
|
||||||
|
TaskHttpMethodPost TaskHTTPMethod = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// 任务
|
||||||
|
type Task struct {
|
||||||
|
Id int `json:"id" xorm:"int pk autoincr"`
|
||||||
|
Name string `json:"name" xorm:"varchar(32) notnull"` // 任务名称
|
||||||
|
Level TaskLevel `json:"level" xorm:"tinyint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务
|
||||||
|
DependencyTaskId string `json:"dependency_task_id" xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔
|
||||||
|
DependencyStatus TaskDependencyStatus `json:"dependency_status" xorm:"tinyint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖
|
||||||
|
Spec string `json:"spec" xorm:"varchar(64) notnull"` // crontab
|
||||||
|
Protocol TaskProtocol `json:"protocol" xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令
|
||||||
|
Command string `json:"command" xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||||
|
HttpMethod TaskHTTPMethod `json:"http_method" xorm:"tinyint notnull default 1"` // http请求方法
|
||||||
|
Timeout int `json:"timeout" xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
|
Multi int8 `json:"multi" xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
||||||
|
RetryTimes int8 `json:"retry_times" xorm:"tinyint notnull default 0"` // 重试次数
|
||||||
|
RetryInterval int16 `json:"retry_interval" xorm:"smallint notnull default 0"` // 重试间隔时间
|
||||||
|
NotifyStatus int8 `json:"notify_status" xorm:"tinyint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知 3: 任务执行结果关键字匹配通知
|
||||||
|
NotifyType int8 `json:"notify_type" xorm:"tinyint notnull default 0"` // 通知类型 1: 邮件 2: slack 3: webhook
|
||||||
|
NotifyReceiverId string `json:"notify_receiver_id" xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
||||||
|
NotifyKeyword string `json:"notify_keyword" xorm:"varchar(128) notnull default '' "`
|
||||||
|
Tag string `json:"tag" xorm:"varchar(32) notnull default ''"`
|
||||||
|
Remark string `json:"remark" xorm:"varchar(100) notnull default ''"` // 备注
|
||||||
|
Status Status `json:"status" xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止
|
||||||
|
Created time.Time `json:"created" xorm:"datetime notnull created"` // 创建时间
|
||||||
|
Deleted time.Time `json:"deleted" xorm:"datetime deleted"` // 删除时间
|
||||||
|
BaseModel `json:"-" xorm:"-"`
|
||||||
|
Hosts []TaskHostDetail `json:"hosts" xorm:"-"`
|
||||||
|
NextRunTime time.Time `json:"next_run_time" xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func taskHostTableName() []string {
|
||||||
|
return []string{TablePrefix + "task_host", "th"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
func (task *Task) Create() (insertId int, err error) {
|
||||||
|
_, err = Db.Insert(task)
|
||||||
|
if err == nil {
|
||||||
|
insertId = task.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) UpdateBean(id int) (int64, error) {
|
||||||
|
return Db.ID(id).
|
||||||
|
Cols(`name,spec,protocol,command,timeout,multi,
|
||||||
|
retry_times,retry_interval,remark,notify_status,
|
||||||
|
notify_type,notify_receiver_id, dependency_task_id, dependency_status, tag,http_method, notify_keyword`).
|
||||||
|
Update(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
func (task *Task) Update(id int, data CommonMap) (int64, error) {
|
||||||
|
return Db.Table(task).ID(id).Update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
func (task *Task) Delete(id int) (int64, error) {
|
||||||
|
return Db.Id(id).Delete(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
func (task *Task) Disable(id int) (int64, error) {
|
||||||
|
return task.Update(id, CommonMap{"status": Disabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 激活
|
||||||
|
func (task *Task) Enable(id int) (int64, error) {
|
||||||
|
return task.Update(id, CommonMap{"status": Enabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取所有激活任务
|
||||||
|
func (task *Task) ActiveList(page, pageSize int) ([]Task, error) {
|
||||||
|
params := CommonMap{"Page": page, "PageSize": pageSize}
|
||||||
|
task.parsePageAndPageSize(params)
|
||||||
|
list := make([]Task, 0)
|
||||||
|
err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).Limit(task.PageSize, task.pageLimitOffset()).
|
||||||
|
Find(&list)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.setHostsForTasks(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某个主机下的所有激活任务
|
||||||
|
func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) {
|
||||||
|
taskHostModel := new(TaskHost)
|
||||||
|
taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list := make([]Task, 0)
|
||||||
|
err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
||||||
|
In("id", taskIds...).
|
||||||
|
Find(&list)
|
||||||
|
if err != nil {
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.setHostsForTasks(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) {
|
||||||
|
taskHostModel := new(TaskHost)
|
||||||
|
var err error
|
||||||
|
for i, value := range tasks {
|
||||||
|
taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tasks[i].Hosts = taskHostDetails
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断任务名称是否存在
|
||||||
|
func (task *Task) NameExist(name string, id int) (bool, error) {
|
||||||
|
if id > 0 {
|
||||||
|
count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task)
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task)
|
||||||
|
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) GetStatus(id int) (Status, error) {
|
||||||
|
exist, err := Db.Id(id).Get(task)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
return 0, errors.New("not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.Status, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) Detail(id int) (Task, error) {
|
||||||
|
t := Task{}
|
||||||
|
_, err := Db.Where("id=?", id).Get(&t)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
taskHostModel := new(TaskHost)
|
||||||
|
t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id)
|
||||||
|
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) List(params CommonMap) ([]Task, error) {
|
||||||
|
task.parsePageAndPageSize(params)
|
||||||
|
list := make([]Task, 0)
|
||||||
|
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||||
|
task.parseWhere(session, params)
|
||||||
|
err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.setHostsForTasks(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取依赖任务列表
|
||||||
|
func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) {
|
||||||
|
list := make([]Task, 0)
|
||||||
|
if ids == "" {
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
idList := strings.Split(ids, ",")
|
||||||
|
taskIds := make([]interface{}, len(idList))
|
||||||
|
for i, v := range idList {
|
||||||
|
taskIds[i] = v
|
||||||
|
}
|
||||||
|
fields := "t.*"
|
||||||
|
err := Db.Alias("t").
|
||||||
|
Where("t.level = ?", TaskLevelChild).
|
||||||
|
In("t.id", taskIds).
|
||||||
|
Cols(fields).
|
||||||
|
Find(&list)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return task.setHostsForTasks(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task *Task) Total(params CommonMap) (int64, error) {
|
||||||
|
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
||||||
|
task.parseWhere(session, params)
|
||||||
|
list := make([]Task, 0)
|
||||||
|
|
||||||
|
err := session.GroupBy("t.id").Find(&list)
|
||||||
|
|
||||||
|
return int64(len(list)), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析where
|
||||||
|
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, ok := params["Id"]
|
||||||
|
if ok && id.(int) > 0 {
|
||||||
|
session.And("t.id = ?", id)
|
||||||
|
}
|
||||||
|
hostId, ok := params["HostId"]
|
||||||
|
if ok && hostId.(int) > 0 {
|
||||||
|
session.And("th.host_id = ?", hostId)
|
||||||
|
}
|
||||||
|
name, ok := params["Name"]
|
||||||
|
if ok && name.(string) != "" {
|
||||||
|
session.And("t.name LIKE ?", "%"+name.(string)+"%")
|
||||||
|
}
|
||||||
|
protocol, ok := params["Protocol"]
|
||||||
|
if ok && protocol.(int) > 0 {
|
||||||
|
session.And("protocol = ?", protocol)
|
||||||
|
}
|
||||||
|
status, ok := params["Status"]
|
||||||
|
if ok && status.(int) > -1 {
|
||||||
|
session.And("status = ?", status)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag, ok := params["Tag"]
|
||||||
|
if ok && tag.(string) != "" {
|
||||||
|
session.And("tag = ? ", tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type TaskHost struct {
|
||||||
|
Id int `json:"id" xorm:"int pk autoincr"`
|
||||||
|
TaskId int `json:"task_id" xorm:"int not null index"`
|
||||||
|
HostId int16 `json:"host_id" xorm:"smallint not null index"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TaskHostDetail struct {
|
||||||
|
TaskHost `xorm:"extends"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Port int `json:"port"`
|
||||||
|
Alias string `json:"alias"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TaskHostDetail) TableName() string {
|
||||||
|
return TablePrefix + "task_host"
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostTableName() []string {
|
||||||
|
return []string{TablePrefix + "host", "h"}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *TaskHost) Remove(taskId int) error {
|
||||||
|
_, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost))
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *TaskHost) Add(taskId int, hostIds []int) error {
|
||||||
|
|
||||||
|
err := th.Remove(taskId)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
taskHosts := make([]TaskHost, len(hostIds))
|
||||||
|
for i, value := range hostIds {
|
||||||
|
taskHosts[i].TaskId = taskId
|
||||||
|
taskHosts[i].HostId = int16(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = Db.Insert(&taskHosts)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
|
||||||
|
list := make([]TaskHostDetail, 0)
|
||||||
|
fields := "th.id,th.host_id,h.alias,h.name,h.port"
|
||||||
|
err := Db.Alias("th").
|
||||||
|
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
||||||
|
Where("th.task_id = ?", taskId).
|
||||||
|
Cols(fields).
|
||||||
|
Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) {
|
||||||
|
list := make([]TaskHost, 0)
|
||||||
|
err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
taskIds := make([]interface{}, len(list))
|
||||||
|
for i, value := range list {
|
||||||
|
taskIds[i] = value.TaskId
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskIds, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断主机id是否有引用
|
||||||
|
func (th *TaskHost) HostIdExist(hostId int16) (bool, error) {
|
||||||
|
count, err := Db.Where("host_id = ?", hostId).Count(th)
|
||||||
|
|
||||||
|
return count > 0, err
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-xorm/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskType int8
|
||||||
|
|
||||||
|
// 任务执行日志
|
||||||
|
type TaskLog struct {
|
||||||
|
Id int64 `json:"id" xorm:"bigint pk autoincr"`
|
||||||
|
TaskId int `json:"task_id" xorm:"int notnull index default 0"` // 任务id
|
||||||
|
Name string `json:"name" xorm:"varchar(32) notnull"` // 任务名称
|
||||||
|
Spec string `json:"spec" xorm:"varchar(64) notnull"` // crontab
|
||||||
|
Protocol TaskProtocol `json:"protocol" xorm:"tinyint notnull index"` // 协议 1:http 2:RPC
|
||||||
|
Command string `json:"command" xorm:"varchar(256) notnull"` // URL地址或shell命令
|
||||||
|
Timeout int `json:"timeout" xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
||||||
|
RetryTimes int8 `json:"retry_times" xorm:"tinyint notnull default 0"` // 任务重试次数
|
||||||
|
Hostname string `json:"hostname" xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔
|
||||||
|
StartTime time.Time `json:"start_time" xorm:"datetime created"` // 开始执行时间
|
||||||
|
EndTime time.Time `json:"end_time" xorm:"datetime updated"` // 执行完成(失败)时间
|
||||||
|
Status Status `json:"status" xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
||||||
|
Result string `json:"result" xorm:"mediumtext notnull defalut '' "` // 执行结果
|
||||||
|
TotalTime int `json:"total_time" xorm:"-"` // 执行总时长
|
||||||
|
BaseModel `json:"-" xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (taskLog *TaskLog) Create() (insertId int64, err error) {
|
||||||
|
_, err = Db.Insert(taskLog)
|
||||||
|
if err == nil {
|
||||||
|
insertId = taskLog.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) {
|
||||||
|
return Db.Table(taskLog).ID(id).Update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
||||||
|
taskLog.parsePageAndPageSize(params)
|
||||||
|
list := make([]TaskLog, 0)
|
||||||
|
session := Db.Desc("id")
|
||||||
|
taskLog.parseWhere(session, params)
|
||||||
|
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
||||||
|
if len(list) > 0 {
|
||||||
|
for i, item := range list {
|
||||||
|
endTime := item.EndTime
|
||||||
|
if item.Status == Running {
|
||||||
|
endTime = time.Now()
|
||||||
|
}
|
||||||
|
execSeconds := endTime.Sub(item.StartTime).Seconds()
|
||||||
|
list[i].TotalTime = int(execSeconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空表
|
||||||
|
func (taskLog *TaskLog) Clear() (int64, error) {
|
||||||
|
return Db.Where("1=1").Delete(taskLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除N个月前的日志
|
||||||
|
func (taskLog *TaskLog) Remove(id int) (int64, error) {
|
||||||
|
t := time.Now().AddDate(0, -id, 0)
|
||||||
|
return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (taskLog *TaskLog) Total(params CommonMap) (int64, error) {
|
||||||
|
session := Db.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
taskLog.parseWhere(session, params)
|
||||||
|
return session.Count(taskLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析where
|
||||||
|
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
|
||||||
|
if len(params) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
taskId, ok := params["TaskId"]
|
||||||
|
if ok && taskId.(int) > 0 {
|
||||||
|
session.And("task_id = ?", taskId)
|
||||||
|
}
|
||||||
|
protocol, ok := params["Protocol"]
|
||||||
|
if ok && protocol.(int) > 0 {
|
||||||
|
session.And("protocol = ?", protocol)
|
||||||
|
}
|
||||||
|
status, ok := params["Status"]
|
||||||
|
if ok && status.(int) > -1 {
|
||||||
|
session.And("status = ?", status)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const PasswordSaltLength = 6
|
||||||
|
|
||||||
|
// 用户model
|
||||||
|
type User struct {
|
||||||
|
Id int `json:"id" xorm:"pk autoincr notnull "`
|
||||||
|
Name string `json:"name" xorm:"varchar(32) notnull unique"` // 用户名
|
||||||
|
Password string `json:"-" xorm:"char(32) notnull "` // 密码
|
||||||
|
Salt string `json:"-" xorm:"char(6) notnull "` // 密码盐值
|
||||||
|
Email string `json:"email" xorm:"varchar(50) notnull unique default '' "` // 邮箱
|
||||||
|
Created time.Time `json:"created" xorm:"datetime notnull created"`
|
||||||
|
Updated time.Time `json:"updated" xorm:"datetime updated"`
|
||||||
|
IsAdmin int8 `json:"is_admin" xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
|
||||||
|
Status Status `json:"status" xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
|
||||||
|
BaseModel `json:"-" xorm:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
func (user *User) Create() (insertId int, err error) {
|
||||||
|
user.Status = Enabled
|
||||||
|
user.Salt = user.generateSalt()
|
||||||
|
user.Password = user.encryptPassword(user.Password, user.Salt)
|
||||||
|
|
||||||
|
_, err = Db.Insert(user)
|
||||||
|
if err == nil {
|
||||||
|
insertId = user.Id
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
func (user *User) Update(id int, data CommonMap) (int64, error) {
|
||||||
|
return Db.Table(user).ID(id).Update(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) UpdatePassword(id int, password string) (int64, error) {
|
||||||
|
salt := user.generateSalt()
|
||||||
|
safePassword := user.encryptPassword(password, salt)
|
||||||
|
|
||||||
|
return user.Update(id, CommonMap{"password": safePassword, "salt": salt})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
func (user *User) Delete(id int) (int64, error) {
|
||||||
|
return Db.Id(id).Delete(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用
|
||||||
|
func (user *User) Disable(id int) (int64, error) {
|
||||||
|
return user.Update(id, CommonMap{"status": Disabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 激活
|
||||||
|
func (user *User) Enable(id int) (int64, error) {
|
||||||
|
return user.Update(id, CommonMap{"status": Enabled})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证用户名和密码
|
||||||
|
func (user *User) Match(username, password string) bool {
|
||||||
|
where := "(name = ? OR email = ?) AND status =? "
|
||||||
|
_, err := Db.Where(where, username, username, Enabled).Get(user)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
hashPassword := user.encryptPassword(password, user.Salt)
|
||||||
|
if hashPassword != user.Password {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户详情
|
||||||
|
func (user *User) Find(id int) error {
|
||||||
|
_, err := Db.Id(id).Get(user)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户名是否存在
|
||||||
|
func (user *User) UsernameExists(username string, uid int) (int64, error) {
|
||||||
|
if uid > 0 {
|
||||||
|
return Db.Where("name = ? AND id != ?", username, uid).Count(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Db.Where("name = ?", username).Count(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 邮箱地址是否存在
|
||||||
|
func (user *User) EmailExists(email string, uid int) (int64, error) {
|
||||||
|
if uid > 0 {
|
||||||
|
return Db.Where("email = ? AND id != ?", email, uid).Count(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Db.Where("email = ?", email).Count(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) List(params CommonMap) ([]User, error) {
|
||||||
|
user.parsePageAndPageSize(params)
|
||||||
|
list := make([]User, 0)
|
||||||
|
err := Db.Desc("id").Limit(user.PageSize, user.pageLimitOffset()).Find(&list)
|
||||||
|
|
||||||
|
return list, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) Total() (int64, error) {
|
||||||
|
return Db.Count(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码加密
|
||||||
|
func (user *User) encryptPassword(password, salt string) string {
|
||||||
|
return utils.Md5(password + salt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成密码盐值
|
||||||
|
func (user *User) generateSalt() string {
|
||||||
|
return utils.RandString(PasswordSaltLength)
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/goutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// AppDir 应用根目录
|
||||||
|
AppDir string // 应用根目录
|
||||||
|
// ConfDir 配置文件目录
|
||||||
|
ConfDir string // 配置目录
|
||||||
|
// LogDir 日志目录
|
||||||
|
LogDir string // 日志目录
|
||||||
|
// AppConfig 配置文件
|
||||||
|
AppConfig string // 应用配置文件
|
||||||
|
// Installed 应用是否已安装
|
||||||
|
Installed bool // 应用是否安装过
|
||||||
|
// Setting 应用配置
|
||||||
|
Setting *setting.Setting // 应用配置
|
||||||
|
// VersionId 版本号
|
||||||
|
VersionId int // 版本号
|
||||||
|
// VersionFile 版本文件
|
||||||
|
VersionFile string // 版本号文件
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitEnv 初始化
|
||||||
|
func InitEnv(versionString string) {
|
||||||
|
logger.InitLogger()
|
||||||
|
var err error
|
||||||
|
AppDir, err = goutil.WorkDir()
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
ConfDir = filepath.Join(AppDir, "/conf")
|
||||||
|
LogDir = filepath.Join(AppDir, "/log")
|
||||||
|
AppConfig = filepath.Join(ConfDir, "/app.ini")
|
||||||
|
VersionFile = filepath.Join(ConfDir, "/.version")
|
||||||
|
createDirIfNotExists(ConfDir, LogDir)
|
||||||
|
Installed = IsInstalled()
|
||||||
|
VersionId = ToNumberVersion(versionString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInstalled 判断应用是否已安装
|
||||||
|
func IsInstalled() bool {
|
||||||
|
_, err := os.Stat(filepath.Join(ConfDir, "/install.lock"))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstallLock 创建安装锁文件
|
||||||
|
func CreateInstallLock() error {
|
||||||
|
_, err := os.Create(filepath.Join(ConfDir, "/install.lock"))
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("创建安装锁文件conf/install.lock失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateVersionFile 更新应用版本号文件
|
||||||
|
func UpdateVersionFile() {
|
||||||
|
err := ioutil.WriteFile(VersionFile,
|
||||||
|
[]byte(strconv.Itoa(VersionId)),
|
||||||
|
0644,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentVersionId 获取应用当前版本号, 从版本号文件中读取
|
||||||
|
func GetCurrentVersionId() int {
|
||||||
|
if !utils.FileExist(VersionFile) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := ioutil.ReadFile(VersionFile)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes)))
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionId
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToNumberVersion 把字符串版本号a.b.c转换为整数版本号abc
|
||||||
|
func ToNumberVersion(versionString string) int {
|
||||||
|
if strings.HasPrefix(versionString, "v") {
|
||||||
|
versionString = versionString[1:]
|
||||||
|
}
|
||||||
|
v := strings.Replace(versionString, ".", "", -1)
|
||||||
|
if len(v) < 3 {
|
||||||
|
v += "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
versionId, err := strconv.Atoi(v)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return versionId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测目录是否存在
|
||||||
|
func createDirIfNotExists(path ...string) {
|
||||||
|
for _, value := range path {
|
||||||
|
if utils.FileExist(value) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := os.Mkdir(value, 0755)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(fmt.Sprintf("创建目录失败:%s", err.Error()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package httpclient
|
||||||
|
|
||||||
|
// http-client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResponseWrapper struct {
|
||||||
|
StatusCode int
|
||||||
|
Body string
|
||||||
|
Header http.Header
|
||||||
|
}
|
||||||
|
|
||||||
|
func Get(url string, timeout int) ResponseWrapper {
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return createRequestError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return request(req, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostParams(url string, params string, timeout int) ResponseWrapper {
|
||||||
|
buf := bytes.NewBufferString(params)
|
||||||
|
req, err := http.NewRequest("POST", url, buf)
|
||||||
|
if err != nil {
|
||||||
|
return createRequestError(err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
return request(req, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostJson(url string, body string, timeout int) ResponseWrapper {
|
||||||
|
buf := bytes.NewBufferString(body)
|
||||||
|
req, err := http.NewRequest("POST", url, buf)
|
||||||
|
if err != nil {
|
||||||
|
return createRequestError(err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-type", "application/json")
|
||||||
|
|
||||||
|
return request(req, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
func request(req *http.Request, timeout int) ResponseWrapper {
|
||||||
|
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
|
||||||
|
client := &http.Client{}
|
||||||
|
if timeout > 0 {
|
||||||
|
client.Timeout = time.Duration(timeout) * time.Second
|
||||||
|
}
|
||||||
|
setRequestHeader(req)
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
wrapper.StatusCode = resp.StatusCode
|
||||||
|
wrapper.Body = string(body)
|
||||||
|
wrapper.Header = resp.Header
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
}
|
||||||
|
|
||||||
|
func setRequestHeader(req *http.Request) {
|
||||||
|
req.Header.Set("User-Agent", "golang/gocron")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRequestError(err error) ResponseWrapper {
|
||||||
|
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
|
||||||
|
return ResponseWrapper{0, errorMessage, make(http.Header)}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/cihub/seelog"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志库
|
||||||
|
|
||||||
|
type Level int8
|
||||||
|
|
||||||
|
var logger seelog.LoggerInterface
|
||||||
|
|
||||||
|
const (
|
||||||
|
DEBUG = iota
|
||||||
|
INFO
|
||||||
|
WARN
|
||||||
|
ERROR
|
||||||
|
FATAL
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitLogger() {
|
||||||
|
config := getLogConfig()
|
||||||
|
l, err := seelog.LoggerFromConfigAsString(config)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
logger = l
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debug(v ...interface{}) {
|
||||||
|
if macaron.Env != macaron.DEV {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
write(DEBUG, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Debugf(format string, v ...interface{}) {
|
||||||
|
if macaron.Env != macaron.DEV {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writef(DEBUG, format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Info(v ...interface{}) {
|
||||||
|
write(INFO, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Infof(format string, v ...interface{}) {
|
||||||
|
writef(INFO, format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warn(v ...interface{}) {
|
||||||
|
write(WARN, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Warnf(format string, v ...interface{}) {
|
||||||
|
writef(WARN, format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Error(v ...interface{}) {
|
||||||
|
write(ERROR, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Errorf(format string, v ...interface{}) {
|
||||||
|
writef(ERROR, format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatal(v ...interface{}) {
|
||||||
|
write(FATAL, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Fatalf(format string, v ...interface{}) {
|
||||||
|
writef(FATAL, format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(level Level, v ...interface{}) {
|
||||||
|
defer logger.Flush()
|
||||||
|
|
||||||
|
content := ""
|
||||||
|
if macaron.Env == macaron.DEV {
|
||||||
|
pc, file, line, ok := runtime.Caller(2)
|
||||||
|
if ok {
|
||||||
|
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case DEBUG:
|
||||||
|
logger.Debug(content, v)
|
||||||
|
case INFO:
|
||||||
|
logger.Info(content, v)
|
||||||
|
case WARN:
|
||||||
|
logger.Warn(content, v)
|
||||||
|
case FATAL:
|
||||||
|
logger.Critical(content, v)
|
||||||
|
os.Exit(1)
|
||||||
|
case ERROR:
|
||||||
|
logger.Error(content, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writef(level Level, format string, v ...interface{}) {
|
||||||
|
defer logger.Flush()
|
||||||
|
|
||||||
|
content := ""
|
||||||
|
if macaron.Env == macaron.DEV {
|
||||||
|
pc, file, line, ok := runtime.Caller(2)
|
||||||
|
if ok {
|
||||||
|
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format = content + format
|
||||||
|
|
||||||
|
switch level {
|
||||||
|
case DEBUG:
|
||||||
|
logger.Debugf(format, v...)
|
||||||
|
case INFO:
|
||||||
|
logger.Infof(format, v...)
|
||||||
|
case WARN:
|
||||||
|
logger.Warnf(format, v...)
|
||||||
|
case FATAL:
|
||||||
|
logger.Criticalf(format, v...)
|
||||||
|
os.Exit(1)
|
||||||
|
case ERROR:
|
||||||
|
logger.Errorf(format, v...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogConfig() string {
|
||||||
|
config := `
|
||||||
|
<seelog>
|
||||||
|
<outputs formatid="main">
|
||||||
|
%s
|
||||||
|
<filter levels="info,critical,error,warn">
|
||||||
|
<file path="log/cron.log" />
|
||||||
|
</filter>
|
||||||
|
</outputs>
|
||||||
|
<formats>
|
||||||
|
<format id="main" format="%%Date/%%Time [%%LEV] %%Msg%%n"/>
|
||||||
|
</formats>
|
||||||
|
</seelog>`
|
||||||
|
|
||||||
|
consoleConfig := ""
|
||||||
|
if macaron.Env == macaron.DEV {
|
||||||
|
consoleConfig =
|
||||||
|
`
|
||||||
|
<filter levels="info,debug,critical,warn,error">
|
||||||
|
<console />
|
||||||
|
</filter>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
config = fmt.Sprintf(config, consoleConfig)
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-gomail/gomail"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @author qiang.ou<qingqianludao@gmail.com>
|
||||||
|
// @date 2017/5/1-00:19
|
||||||
|
|
||||||
|
type Mail struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mail *Mail) Send(msg Message) {
|
||||||
|
model := new(models.Setting)
|
||||||
|
mailSetting, err := model.Mail()
|
||||||
|
logger.Debugf("%+v", mailSetting)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("#mail#从数据库获取mail配置失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mailSetting.Host == "" {
|
||||||
|
logger.Error("#mail#Host为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mailSetting.Port == 0 {
|
||||||
|
logger.Error("#mail#Port为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mailSetting.User == "" {
|
||||||
|
logger.Error("#mail#User为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if mailSetting.Password == "" {
|
||||||
|
logger.Error("#mail#Password为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg["content"] = parseNotifyTemplate(mailSetting.Template, msg)
|
||||||
|
toUsers := mail.getActiveMailUsers(mailSetting, msg)
|
||||||
|
mail.send(mailSetting, toUsers, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) {
|
||||||
|
body := msg["content"].(string)
|
||||||
|
body = strings.Replace(body, "\n", "<br>", -1)
|
||||||
|
gomailMessage := gomail.NewMessage()
|
||||||
|
gomailMessage.SetHeader("From", mailSetting.User)
|
||||||
|
gomailMessage.SetHeader("To", toUsers...)
|
||||||
|
gomailMessage.SetHeader("Subject", "gocron-定时任务通知")
|
||||||
|
gomailMessage.SetBody("text/html", body)
|
||||||
|
mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port,
|
||||||
|
mailSetting.User, mailSetting.Password)
|
||||||
|
maxTimes := 3
|
||||||
|
i := 0
|
||||||
|
for i < maxTimes {
|
||||||
|
err := mailer.DialAndSend(gomailMessage)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
if i < maxTimes {
|
||||||
|
logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string {
|
||||||
|
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||||
|
users := []string{}
|
||||||
|
for _, v := range mailSetting.MailUsers {
|
||||||
|
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||||
|
users = append(users, v.Email)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Message map[string]interface{}
|
||||||
|
|
||||||
|
type Notifiable interface {
|
||||||
|
Send(msg Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
var queue = make(chan Message, 100)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
go run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 把消息推入队列
|
||||||
|
func Push(msg Message) {
|
||||||
|
queue <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() {
|
||||||
|
for msg := range queue {
|
||||||
|
// 根据任务配置发送通知
|
||||||
|
taskType, taskTypeOk := msg["task_type"]
|
||||||
|
_, taskReceiverIdOk := msg["task_receiver_id"]
|
||||||
|
_, nameOk := msg["name"]
|
||||||
|
_, outputOk := msg["output"]
|
||||||
|
_, statusOk := msg["status"]
|
||||||
|
if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk {
|
||||||
|
logger.Errorf("#notify#参数不完整#%+v", msg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"])
|
||||||
|
logger.Debugf("%+v", msg)
|
||||||
|
switch taskType.(int8) {
|
||||||
|
case 1:
|
||||||
|
// 邮件
|
||||||
|
mail := Mail{}
|
||||||
|
go mail.Send(msg)
|
||||||
|
case 2:
|
||||||
|
// Slack
|
||||||
|
slack := Slack{}
|
||||||
|
go slack.Send(msg)
|
||||||
|
case 3:
|
||||||
|
// WebHook
|
||||||
|
webHook := WebHook{}
|
||||||
|
go webHook.Send(msg)
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNotifyTemplate(notifyTemplate string, msg Message) string {
|
||||||
|
tmpl, err := template.New("notify").Parse(notifyTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Sprintf("解析通知模板失败: %s", err)
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
tmpl.Execute(&buf, map[string]interface{}{
|
||||||
|
"TaskId": msg["task_id"],
|
||||||
|
"TaskName": msg["name"],
|
||||||
|
"Status": msg["status"],
|
||||||
|
"Result": msg["output"],
|
||||||
|
})
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
// 发送消息到slack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"html"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/httpclient"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Slack struct{}
|
||||||
|
|
||||||
|
func (slack *Slack) Send(msg Message) {
|
||||||
|
model := new(models.Setting)
|
||||||
|
slackSetting, err := model.Slack()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("#slack#从数据库获取slack配置失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if slackSetting.Url == "" {
|
||||||
|
logger.Error("#slack#webhook-url为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(slackSetting.Channels) == 0 {
|
||||||
|
logger.Error("#slack#channels配置为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debugf("%+v", slackSetting)
|
||||||
|
channels := slack.getActiveSlackChannels(slackSetting, msg)
|
||||||
|
logger.Debugf("%+v", channels)
|
||||||
|
msg["content"] = parseNotifyTemplate(slackSetting.Template, msg)
|
||||||
|
msg["content"] = html.UnescapeString(msg["content"].(string))
|
||||||
|
for _, channel := range channels {
|
||||||
|
slack.send(msg, slackSetting.Url, channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slack *Slack) send(msg Message, slackUrl string, channel string) {
|
||||||
|
formatBody := slack.format(msg["content"].(string), channel)
|
||||||
|
timeout := 30
|
||||||
|
maxTimes := 3
|
||||||
|
i := 0
|
||||||
|
for i < maxTimes {
|
||||||
|
resp := httpclient.PostJson(slackUrl, formatBody, timeout)
|
||||||
|
if resp.StatusCode == 200 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
if i < maxTimes {
|
||||||
|
logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string {
|
||||||
|
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
||||||
|
channels := []string{}
|
||||||
|
for _, v := range slackSetting.Channels {
|
||||||
|
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
||||||
|
channels = append(channels, v.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化消息内容
|
||||||
|
func (slack *Slack) format(content string, channel string) string {
|
||||||
|
content = utils.EscapeJson(content)
|
||||||
|
specialChars := []string{"&", "<", ">"}
|
||||||
|
replaceChars := []string{"&", "<", ">"}
|
||||||
|
content = utils.ReplaceStrings(content, specialChars, replaceChars)
|
||||||
|
|
||||||
|
return fmt.Sprintf(`{"text":"%s","username":"gocron", "channel":"%s"}`, content, channel)
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package notify
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/httpclient"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebHook struct{}
|
||||||
|
|
||||||
|
func (webHook *WebHook) Send(msg Message) {
|
||||||
|
model := new(models.Setting)
|
||||||
|
webHookSetting, err := model.Webhook()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("#webHook#从数据库获取webHook配置失败", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if webHookSetting.Url == "" {
|
||||||
|
logger.Error("#webHook#webhook-url为空")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debugf("%+v", webHookSetting)
|
||||||
|
msg["name"] = utils.EscapeJson(msg["name"].(string))
|
||||||
|
msg["output"] = utils.EscapeJson(msg["output"].(string))
|
||||||
|
msg["content"] = parseNotifyTemplate(webHookSetting.Template, msg)
|
||||||
|
msg["content"] = html.UnescapeString(msg["content"].(string))
|
||||||
|
webHook.send(msg, webHookSetting.Url)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (webHook *WebHook) send(msg Message, url string) {
|
||||||
|
content := msg["content"].(string)
|
||||||
|
timeout := 30
|
||||||
|
maxTimes := 3
|
||||||
|
i := 0
|
||||||
|
for i < maxTimes {
|
||||||
|
resp := httpclient.PostJson(url, content, timeout)
|
||||||
|
if resp.StatusCode == 200 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
if i < maxTimes {
|
||||||
|
logger.Errorf("webHook#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Certificate struct {
|
||||||
|
CAFile string
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
ServerName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Certificate) GetTLSConfigForServer() (*tls.Config, error) {
|
||||||
|
certificate, err := tls.LoadX509KeyPair(
|
||||||
|
c.CertFile,
|
||||||
|
c.KeyFile,
|
||||||
|
)
|
||||||
|
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
bs, err := ioutil.ReadFile(c.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("failed to read client ca cert: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := certPool.AppendCertsFromPEM(bs)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to append client certs")
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConfig := &tls.Config{
|
||||||
|
ClientAuth: tls.RequireAndVerifyClientCert,
|
||||||
|
Certificates: []tls.Certificate{certificate},
|
||||||
|
ClientCAs: certPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
return tlsConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Certificate) GetTransportCredsForClient() (credentials.TransportCredentials, error) {
|
||||||
|
certificate, err := tls.LoadX509KeyPair(
|
||||||
|
c.CertFile,
|
||||||
|
c.KeyFile,
|
||||||
|
)
|
||||||
|
|
||||||
|
certPool := x509.NewCertPool()
|
||||||
|
bs, err := ioutil.ReadFile(c.CAFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(fmt.Sprintf("failed to read ca cert: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
ok := certPool.AppendCertsFromPEM(bs)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to append certs")
|
||||||
|
}
|
||||||
|
|
||||||
|
transportCreds := credentials.NewTLS(&tls.Config{
|
||||||
|
ServerName: c.ServerName,
|
||||||
|
Certificates: []tls.Certificate{certificate},
|
||||||
|
RootCAs: certPool,
|
||||||
|
})
|
||||||
|
|
||||||
|
return transportCreds, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/grpcpool"
|
||||||
|
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
taskMap sync.Map
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errUnavailable = errors.New("无法连接远程服务器")
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateTaskUniqueKey(ip string, port int, id int64) string {
|
||||||
|
return fmt.Sprintf("%s:%d:%d", ip, port, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Stop(ip string, port int, id int64) {
|
||||||
|
key := generateTaskUniqueKey(ip, port, id)
|
||||||
|
cancel, ok := taskMap.Load(key)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cancel.(context.CancelFunc)()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Error("panic#rpc/client.go:Exec#", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||||
|
conn, err := grpcpool.Pool.Get(addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
isConnClosed := false
|
||||||
|
defer func() {
|
||||||
|
if !isConnClosed {
|
||||||
|
grpcpool.Pool.Put(addr, conn)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
c := pb.NewTaskClient(conn)
|
||||||
|
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
||||||
|
taskReq.Timeout = 86400
|
||||||
|
}
|
||||||
|
timeout := time.Duration(taskReq.Timeout) * time.Second
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
taskUniqueKey := generateTaskUniqueKey(ip, port, taskReq.Id)
|
||||||
|
taskMap.Store(taskUniqueKey, cancel)
|
||||||
|
defer taskMap.Delete(taskUniqueKey)
|
||||||
|
resp, err := c.Run(ctx, taskReq)
|
||||||
|
if err != nil {
|
||||||
|
return parseGRPCError(err, conn, &isConnClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Error == "" {
|
||||||
|
return resp.Output, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Output, errors.New(resp.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) {
|
||||||
|
switch grpc.Code(err) {
|
||||||
|
case codes.Unavailable, codes.Internal:
|
||||||
|
conn.Close()
|
||||||
|
*connClosed = true
|
||||||
|
return "", errUnavailable
|
||||||
|
case codes.DeadlineExceeded:
|
||||||
|
return "", errors.New("执行超时, 强制结束")
|
||||||
|
case codes.Canceled:
|
||||||
|
return "", errors.New("手动停止")
|
||||||
|
}
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
package grpcpool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
||||||
|
"github.com/silenceper/pool"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Pool GRPCPool
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidConn = errors.New("invalid connection")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Pool = GRPCPool{
|
||||||
|
make(map[string]pool.Pool),
|
||||||
|
sync.RWMutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GRPCPool struct {
|
||||||
|
// map key格式 ip:port
|
||||||
|
conns map[string]pool.Pool
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
||||||
|
p.RLock()
|
||||||
|
pool, ok := p.conns[addr]
|
||||||
|
p.RUnlock()
|
||||||
|
if !ok {
|
||||||
|
err := p.newCommonPool(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.RLock()
|
||||||
|
pool = p.conns[addr]
|
||||||
|
p.RUnlock()
|
||||||
|
conn, err := pool.Get()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return conn.(*grpc.ClientConn), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
|
||||||
|
p.RLock()
|
||||||
|
defer p.RUnlock()
|
||||||
|
pool, ok := p.conns[addr]
|
||||||
|
if ok {
|
||||||
|
return pool.Put(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrInvalidConn
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放连接池
|
||||||
|
func (p *GRPCPool) Release(addr string) {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
pool, ok := p.conns[addr]
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pool.Release()
|
||||||
|
delete(p.conns, addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放所有连接池
|
||||||
|
func (p *GRPCPool) ReleaseAll() {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
for _, pool := range p.conns {
|
||||||
|
pool.Release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化底层连接池
|
||||||
|
func (p *GRPCPool) newCommonPool(addr string) error {
|
||||||
|
p.Lock()
|
||||||
|
defer p.Unlock()
|
||||||
|
commonPool, ok := p.conns[addr]
|
||||||
|
if ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
poolConfig := &pool.PoolConfig{
|
||||||
|
InitialCap: 1,
|
||||||
|
MaxCap: 30,
|
||||||
|
Factory: func() (interface{}, error) {
|
||||||
|
if !app.Setting.EnableTLS {
|
||||||
|
return grpc.Dial(addr, grpc.WithInsecure())
|
||||||
|
}
|
||||||
|
|
||||||
|
server := strings.Split(addr, ":")
|
||||||
|
|
||||||
|
certificate := auth.Certificate{
|
||||||
|
CAFile: app.Setting.CAFile,
|
||||||
|
CertFile: app.Setting.CertFile,
|
||||||
|
KeyFile: app.Setting.KeyFile,
|
||||||
|
ServerName: server[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
transportCreds, err := certificate.GetTransportCredsForClient()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds))
|
||||||
|
},
|
||||||
|
Close: func(v interface{}) error {
|
||||||
|
conn, ok := v.(*grpc.ClientConn)
|
||||||
|
if ok && conn != nil {
|
||||||
|
return conn.Close()
|
||||||
|
}
|
||||||
|
return ErrInvalidConn
|
||||||
|
},
|
||||||
|
IdleTimeout: 3 * time.Minute,
|
||||||
|
}
|
||||||
|
|
||||||
|
commonPool, err := pool.NewChannelPool(poolConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.conns[addr] = commonPool
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -36,6 +36,7 @@ const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
|||||||
type TaskRequest struct {
|
type TaskRequest struct {
|
||||||
Command string `protobuf:"bytes,2,opt,name=command" json:"command,omitempty"`
|
Command string `protobuf:"bytes,2,opt,name=command" json:"command,omitempty"`
|
||||||
Timeout int32 `protobuf:"varint,3,opt,name=timeout" json:"timeout,omitempty"`
|
Timeout int32 `protobuf:"varint,3,opt,name=timeout" json:"timeout,omitempty"`
|
||||||
|
Id int64 `protobuf:"varint,4,opt,name=id" json:"id,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *TaskRequest) Reset() { *m = TaskRequest{} }
|
func (m *TaskRequest) Reset() { *m = TaskRequest{} }
|
||||||
@@ -57,6 +58,13 @@ func (m *TaskRequest) GetTimeout() int32 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *TaskRequest) GetId() int64 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Id
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
type TaskResponse struct {
|
type TaskResponse struct {
|
||||||
Output string `protobuf:"bytes,1,opt,name=output" json:"output,omitempty"`
|
Output string `protobuf:"bytes,1,opt,name=output" json:"output,omitempty"`
|
||||||
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
Error string `protobuf:"bytes,2,opt,name=error" json:"error,omitempty"`
|
||||||
@@ -161,16 +169,17 @@ var _Task_serviceDesc = grpc.ServiceDesc{
|
|||||||
func init() { proto.RegisterFile("task.proto", fileDescriptor0) }
|
func init() { proto.RegisterFile("task.proto", fileDescriptor0) }
|
||||||
|
|
||||||
var fileDescriptor0 = []byte{
|
var fileDescriptor0 = []byte{
|
||||||
// 170 bytes of a gzipped FileDescriptorProto
|
// 184 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xb1, 0x0e, 0x82, 0x40,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xcf, 0x8a, 0x83, 0x30,
|
||||||
0x0c, 0x86, 0x45, 0x04, 0x63, 0x75, 0xd0, 0xc6, 0x98, 0x8b, 0x13, 0x61, 0x62, 0x30, 0x0c, 0xea,
|
0x10, 0xc6, 0x37, 0x46, 0x5d, 0x76, 0x76, 0x59, 0xda, 0xa1, 0x94, 0xd0, 0x93, 0x78, 0xf2, 0x50,
|
||||||
0xe8, 0xe2, 0x2b, 0x5c, 0x7c, 0x01, 0xc4, 0x1b, 0x0c, 0x81, 0x9e, 0xbd, 0xde, 0xfb, 0x1b, 0x38,
|
0x3c, 0xb4, 0x3d, 0xf6, 0x25, 0x1a, 0xfa, 0x02, 0x56, 0x73, 0x10, 0xd1, 0x49, 0x93, 0xc9, 0xfb,
|
||||||
0x48, 0x18, 0xbf, 0x36, 0xfd, 0xfa, 0xff, 0x00, 0x52, 0xb9, 0xa6, 0xb4, 0x4c, 0x42, 0x18, 0xb3,
|
0x17, 0xff, 0x81, 0xc7, 0xdf, 0x0c, 0xf3, 0xfd, 0xe6, 0x03, 0xe0, 0xca, 0x77, 0xa5, 0x75, 0xc4,
|
||||||
0xad, 0xf3, 0x27, 0x6c, 0x5f, 0x95, 0x6b, 0xb4, 0xf9, 0x79, 0xe3, 0x04, 0x15, 0xac, 0x6b, 0x6a,
|
0x84, 0xd2, 0xd9, 0x3a, 0x7f, 0xc0, 0xef, 0xb3, 0xf2, 0x9d, 0x36, 0xef, 0x60, 0x3c, 0xa3, 0x82,
|
||||||
0xdb, 0xaa, 0xfb, 0xa8, 0x65, 0x16, 0x15, 0x1b, 0x3d, 0x61, 0xbf, 0x91, 0x6f, 0x6b, 0xc8, 0x8b,
|
0xef, 0x9a, 0xfa, 0xbe, 0x1a, 0x1a, 0x15, 0x65, 0xa2, 0xf8, 0xd1, 0x2b, 0x8e, 0x1b, 0x6e, 0x7b,
|
||||||
0x8a, 0xb3, 0xa8, 0x48, 0xf4, 0x84, 0xf9, 0x03, 0x76, 0x41, 0xe1, 0x2c, 0x75, 0xce, 0xe0, 0x09,
|
0x43, 0x81, 0x95, 0xcc, 0x44, 0x91, 0xe8, 0x15, 0xf1, 0x1f, 0xa2, 0xb6, 0x51, 0x71, 0x26, 0x0a,
|
||||||
0x52, 0xf2, 0x62, 0xbd, 0xa8, 0x68, 0x50, 0x8c, 0x84, 0x47, 0x48, 0x0c, 0x33, 0xf1, 0x68, 0x0e,
|
0xa9, 0xa3, 0xb6, 0xc9, 0xef, 0xf0, 0x37, 0x47, 0x7a, 0x4b, 0x83, 0x37, 0x78, 0x84, 0x94, 0x02,
|
||||||
0x70, 0xbd, 0xc3, 0xaa, 0xbf, 0xc6, 0x0b, 0xc4, 0xda, 0x77, 0xb8, 0x2f, 0xd9, 0xd6, 0xe5, 0x2c,
|
0xdb, 0xc0, 0x4a, 0x4c, 0x91, 0x0b, 0xe1, 0x01, 0x12, 0xe3, 0x1c, 0xb9, 0xc5, 0x34, 0xc3, 0xe5,
|
||||||
0xd2, 0xf9, 0x30, 0x9b, 0x84, 0x0f, 0xf9, 0xe2, 0x9d, 0x0e, 0x15, 0x6e, 0xff, 0x00, 0x00, 0x00,
|
0x06, 0xf1, 0x78, 0x8d, 0x67, 0x90, 0x3a, 0x0c, 0xb8, 0x2b, 0x9d, 0xad, 0xcb, 0xcd, 0x8b, 0xa7,
|
||||||
0xff, 0xff, 0xef, 0x3c, 0x71, 0x6b, 0xd0, 0x00, 0x00, 0x00,
|
0xfd, 0x66, 0x32, 0x1b, 0xf2, 0xaf, 0x57, 0x3a, 0x55, 0xba, 0x7e, 0x02, 0x00, 0x00, 0xff, 0xff,
|
||||||
|
0xd7, 0x7f, 0x8a, 0x9d, 0xe0, 0x00, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
@@ -9,6 +9,7 @@ service Task {
|
|||||||
message TaskRequest {
|
message TaskRequest {
|
||||||
string command = 2; // 命令
|
string command = 2; // 命令
|
||||||
int32 timeout = 3; // 任务执行超时时间
|
int32 timeout = 3; // 任务执行超时时间
|
||||||
|
int64 id = 4; // 执行任务唯一ID
|
||||||
}
|
}
|
||||||
|
|
||||||
message TaskResponse {
|
message TaskResponse {
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
||||||
|
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct{}
|
||||||
|
|
||||||
|
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
grpclog.Println(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
output, err := utils.ExecShell(ctx, req.Command)
|
||||||
|
resp := new(pb.TaskResponse)
|
||||||
|
resp.Output = output
|
||||||
|
if err != nil {
|
||||||
|
resp.Error = err.Error()
|
||||||
|
} else {
|
||||||
|
resp.Error = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(addr string, enableTLS bool, certificate auth.Certificate) {
|
||||||
|
l, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s *grpc.Server
|
||||||
|
if enableTLS {
|
||||||
|
tlsConfig, err := certificate.GetTLSConfigForServer()
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Fatal(err)
|
||||||
|
}
|
||||||
|
opt := grpc.Creds(credentials.NewTLS(tlsConfig))
|
||||||
|
s = grpc.NewServer(opt)
|
||||||
|
pb.RegisterTaskServer(s, Server{})
|
||||||
|
grpclog.Printf("listen %s with TLS", addr)
|
||||||
|
} else {
|
||||||
|
s = grpc.NewServer()
|
||||||
|
pb.RegisterTaskServer(s, Server{})
|
||||||
|
grpclog.Printf("listen %s", addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.Serve(l)
|
||||||
|
grpclog.Fatal(err)
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultSection = "default"
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Db struct {
|
||||||
|
Engine string
|
||||||
|
Host string
|
||||||
|
Port int
|
||||||
|
User string
|
||||||
|
Password string
|
||||||
|
Database string
|
||||||
|
Prefix string
|
||||||
|
Charset string
|
||||||
|
MaxIdleConns int
|
||||||
|
MaxOpenConns int
|
||||||
|
}
|
||||||
|
AllowIps string
|
||||||
|
AppName string
|
||||||
|
ApiKey string
|
||||||
|
ApiSecret string
|
||||||
|
ApiSignEnable bool
|
||||||
|
|
||||||
|
EnableTLS bool
|
||||||
|
CAFile string
|
||||||
|
CertFile string
|
||||||
|
KeyFile string
|
||||||
|
|
||||||
|
ConcurrencyQueue int
|
||||||
|
AuthSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取配置
|
||||||
|
func Read(filename string) (*Setting, error) {
|
||||||
|
config, err := ini.Load(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
section := config.Section(DefaultSection)
|
||||||
|
|
||||||
|
var s Setting
|
||||||
|
|
||||||
|
s.Db.Engine = section.Key("db.engine").MustString("mysql")
|
||||||
|
s.Db.Host = section.Key("db.host").MustString("127.0.0.1")
|
||||||
|
s.Db.Port = section.Key("db.port").MustInt(3306)
|
||||||
|
s.Db.User = section.Key("db.user").MustString("")
|
||||||
|
s.Db.Password = section.Key("db.password").MustString("")
|
||||||
|
s.Db.Database = section.Key("db.database").MustString("gocron")
|
||||||
|
s.Db.Prefix = section.Key("db.prefix").MustString("")
|
||||||
|
s.Db.Charset = section.Key("db.charset").MustString("utf8")
|
||||||
|
s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30)
|
||||||
|
s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100)
|
||||||
|
|
||||||
|
s.AllowIps = section.Key("allow_ips").MustString("")
|
||||||
|
s.AppName = section.Key("app.name").MustString("定时任务管理系统")
|
||||||
|
s.ApiKey = section.Key("api.key").MustString("")
|
||||||
|
s.ApiSecret = section.Key("api.secret").MustString("")
|
||||||
|
s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true)
|
||||||
|
s.ConcurrencyQueue = section.Key("concurrency.queue").MustInt(500)
|
||||||
|
s.AuthSecret = section.Key("auth_secret").MustString("")
|
||||||
|
if s.AuthSecret == "" {
|
||||||
|
s.AuthSecret = utils.RandAuthToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
s.EnableTLS = section.Key("enable_tls").MustBool(false)
|
||||||
|
s.CAFile = section.Key("ca_file").MustString("")
|
||||||
|
s.CertFile = section.Key("cert_file").MustString("")
|
||||||
|
s.KeyFile = section.Key("key_file").MustString("")
|
||||||
|
|
||||||
|
if s.EnableTLS {
|
||||||
|
if !utils.FileExist(s.CAFile) {
|
||||||
|
logger.Fatalf("failed to read ca cert file: %s", s.CAFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.FileExist(s.CertFile) {
|
||||||
|
logger.Fatalf("failed to read client cert file: %s", s.CertFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !utils.FileExist(s.KeyFile) {
|
||||||
|
logger.Fatalf("failed to read client key file: %s", s.KeyFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入配置
|
||||||
|
func Write(config []string, filename string) error {
|
||||||
|
if len(config) == 0 {
|
||||||
|
return errors.New("参数不能为空")
|
||||||
|
}
|
||||||
|
if len(config)%2 != 0 {
|
||||||
|
return errors.New("参数不匹配")
|
||||||
|
}
|
||||||
|
|
||||||
|
file := ini.Empty()
|
||||||
|
|
||||||
|
section, err := file.NewSection(DefaultSection)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := 0; i < len(config); {
|
||||||
|
_, err = section.NewKey(config[i], config[i+1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
err = file.SaveTo(filename)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
// json 格式输出
|
||||||
|
|
||||||
|
type response struct {
|
||||||
|
Code int `json:"code"` // 状态码 0:成功 非0:失败
|
||||||
|
Message string `json:"message"` // 信息
|
||||||
|
Data interface{} `json:"data"` // 数据
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonResponse struct{}
|
||||||
|
|
||||||
|
const ResponseSuccess = 0
|
||||||
|
const ResponseFailure = 1
|
||||||
|
const UnauthorizedError = 403
|
||||||
|
const AuthError = 401
|
||||||
|
const NotFound = 404
|
||||||
|
const ServerError = 500
|
||||||
|
const AppNotInstall = 801
|
||||||
|
|
||||||
|
const SuccessContent = "操作成功"
|
||||||
|
const FailureContent = "操作失败"
|
||||||
|
|
||||||
|
func JsonResponseByErr(err error) string {
|
||||||
|
jsonResp := JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return jsonResp.CommonFailure(FailureContent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success(SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JsonResponse) Success(message string, data interface{}) string {
|
||||||
|
return j.response(ResponseSuccess, message, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JsonResponse) Failure(code int, message string) string {
|
||||||
|
return j.response(code, message, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JsonResponse) CommonFailure(message string, err ...error) string {
|
||||||
|
if len(err) > 0 {
|
||||||
|
logger.Warn(err)
|
||||||
|
}
|
||||||
|
return j.Failure(ResponseFailure, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *JsonResponse) response(code int, message string, data interface{}) string {
|
||||||
|
resp := response{
|
||||||
|
Code: code,
|
||||||
|
Message: message,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
crand "crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Tang-RoseChild/mahonia"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RandAuthToken() string {
|
||||||
|
buf := make([]byte, 32)
|
||||||
|
_, err := crand.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return RandString(64)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成长度为length的随机字符串
|
||||||
|
func RandString(length int64) string {
|
||||||
|
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
|
result := []byte{}
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
sourceLength := len(sources)
|
||||||
|
var i int64 = 0
|
||||||
|
for ; i < length; i++ {
|
||||||
|
result = append(result, sources[r.Intn(sourceLength)])
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成32位MD5摘要
|
||||||
|
func Md5(str string) string {
|
||||||
|
m := md5.New()
|
||||||
|
m.Write([]byte(str))
|
||||||
|
|
||||||
|
return hex.EncodeToString(m.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成0-max之间随机数
|
||||||
|
func RandNumber(max int) int {
|
||||||
|
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
|
||||||
|
return r.Intn(max)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GBK编码转换为UTF8
|
||||||
|
func GBK2UTF8(s string) (string, bool) {
|
||||||
|
dec := mahonia.NewDecoder("gbk")
|
||||||
|
|
||||||
|
return dec.ConvertStringOK(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量替换字符串
|
||||||
|
func ReplaceStrings(s string, old []string, replace []string) string {
|
||||||
|
if s == "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if len(old) != len(replace) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, v := range old {
|
||||||
|
s = strings.Replace(s, v, replace[i], 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func InStringSlice(slice []string, element string) bool {
|
||||||
|
element = strings.TrimSpace(element)
|
||||||
|
for _, v := range slice {
|
||||||
|
if strings.TrimSpace(v) == element {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转义json特殊字符
|
||||||
|
func EscapeJson(s string) string {
|
||||||
|
specialChars := []string{"\\", "\b", "\f", "\n", "\r", "\t", "\""}
|
||||||
|
replaceChars := []string{"\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\""}
|
||||||
|
|
||||||
|
return ReplaceStrings(s, specialChars, replaceChars)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件是否存在及是否有权限访问
|
||||||
|
func FileExist(file string) bool {
|
||||||
|
_, err := os.Stat(file)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if os.IsPermission(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestRandString(t *testing.T) {
|
||||||
|
str := RandString(32)
|
||||||
|
if len(str) != 32 {
|
||||||
|
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMd5(t *testing.T) {
|
||||||
|
str := Md5("123456")
|
||||||
|
if len(str) != 32 {
|
||||||
|
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRandNumber(t *testing.T) {
|
||||||
|
num := RandNumber(10000)
|
||||||
|
if num <= 0 && num >= 10000 {
|
||||||
|
t.Fatalf("随机数不在有效范围内-%d", num)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
output string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行shell命令,可设置执行超时时间
|
||||||
|
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||||
|
cmd := exec.Command("/bin/bash", "-c", command)
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setpgid: true,
|
||||||
|
}
|
||||||
|
resultChan := make(chan Result)
|
||||||
|
go func() {
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
resultChan <- Result{string(output), err}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
if cmd.Process.Pid > 0 {
|
||||||
|
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||||
|
}
|
||||||
|
return "", errors.New("timeout killed")
|
||||||
|
case result := <-resultChan:
|
||||||
|
return result.output, result.err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
output string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行shell命令,可设置执行超时时间
|
||||||
|
func ExecShell(ctx context.Context, command string) (string, error) {
|
||||||
|
cmd := exec.Command("cmd", "/C", command)
|
||||||
|
// 隐藏cmd窗口
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
HideWindow: true,
|
||||||
|
}
|
||||||
|
var resultChan chan Result = make(chan Result)
|
||||||
|
go func() {
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
resultChan <- Result{string(output), err}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
if cmd.Process.Pid > 0 {
|
||||||
|
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
|
||||||
|
cmd.Process.Kill()
|
||||||
|
}
|
||||||
|
return "", errors.New("timeout killed")
|
||||||
|
case result := <-resultChan:
|
||||||
|
return ConvertEncoding(result.output), result.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertEncoding(outputGBK string) string {
|
||||||
|
// windows平台编码为gbk,需转换为utf8才能入库
|
||||||
|
outputUTF8, ok := GBK2UTF8(outputGBK)
|
||||||
|
if ok {
|
||||||
|
return outputUTF8
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputGBK
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParsePageAndPageSize 解析查询参数中的页数和每页数量
|
||||||
|
func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) {
|
||||||
|
page := ctx.QueryInt("page")
|
||||||
|
pageSize := ctx.QueryInt("page_size")
|
||||||
|
if page <= 0 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if pageSize <= 0 {
|
||||||
|
pageSize = models.PageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
params["Page"] = page
|
||||||
|
params["PageSize"] = pageSize
|
||||||
|
}
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
package host
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-macaron/binding"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/client"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/grpcpool"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/base"
|
||||||
|
"github.com/ouqiang/gocron/internal/service"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const testConnectionCommand = "echo hello"
|
||||||
|
const testConnectionTimeout = 5
|
||||||
|
|
||||||
|
// Index 主机列表
|
||||||
|
func Index(ctx *macaron.Context) string {
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
queryParams := parseQueryParams(ctx)
|
||||||
|
total, err := hostModel.Total(queryParams)
|
||||||
|
hosts, err := hostModel.List(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"total": total,
|
||||||
|
"data": hosts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// All 获取所有主机
|
||||||
|
func All(ctx *macaron.Context) string {
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
hostModel.PageSize = -1
|
||||||
|
hosts, err := hostModel.List(models.CommonMap{})
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, hosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 主机详情
|
||||||
|
func Detail(ctx *macaron.Context) string {
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
err := hostModel.Find(id)
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if err != nil || hostModel.Id == 0 {
|
||||||
|
logger.Errorf("获取主机详情失败#主机id-%d", id)
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, hostModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HostForm struct {
|
||||||
|
Id int16
|
||||||
|
Name string `binding:"Required;MaxSize(64)"`
|
||||||
|
Alias string `binding:"Required;MaxSize(32)"`
|
||||||
|
Port int `binding:"Required;Range(1-65535)"`
|
||||||
|
Remark string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error 表单验证错误处理
|
||||||
|
func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||||
|
ctx.Write([]byte(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store 保存、修改主机信息
|
||||||
|
func Store(ctx *macaron.Context, form HostForm) string {
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
id := form.Id
|
||||||
|
nameExist, err := hostModel.NameExists(form.Name, form.Id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("操作失败", err)
|
||||||
|
}
|
||||||
|
if nameExist {
|
||||||
|
return json.CommonFailure("主机名已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
hostModel.Name = strings.TrimSpace(form.Name)
|
||||||
|
hostModel.Alias = strings.TrimSpace(form.Alias)
|
||||||
|
hostModel.Port = form.Port
|
||||||
|
hostModel.Remark = strings.TrimSpace(form.Remark)
|
||||||
|
isCreate := false
|
||||||
|
oldHostModel := new(models.Host)
|
||||||
|
err = oldHostModel.Find(int(id))
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("主机不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if id > 0 {
|
||||||
|
_, err = hostModel.UpdateBean(id)
|
||||||
|
} else {
|
||||||
|
isCreate = true
|
||||||
|
id, err = hostModel.Create()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("保存失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isCreate {
|
||||||
|
oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port)
|
||||||
|
newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||||
|
if oldAddr != newAddr {
|
||||||
|
grpcpool.Pool.Release(oldAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
tasks, err := taskModel.ActiveListByHostId(id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("刷新任务主机信息失败", err)
|
||||||
|
}
|
||||||
|
service.ServiceTask.BatchAdd(tasks)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("保存成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove 删除主机
|
||||||
|
func Remove(ctx *macaron.Context) string {
|
||||||
|
id, err := strconv.Atoi(ctx.Params(":id"))
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("参数错误", err)
|
||||||
|
}
|
||||||
|
taskHostModel := new(models.TaskHost)
|
||||||
|
exist, err := taskHostModel.HostIdExist(int16(id))
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("操作失败", err)
|
||||||
|
}
|
||||||
|
if exist {
|
||||||
|
return json.CommonFailure("有任务引用此主机,不能删除")
|
||||||
|
}
|
||||||
|
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
err = hostModel.Find(int(id))
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("主机不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = hostModel.Delete(id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("操作失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port)
|
||||||
|
grpcpool.Pool.Release(addr)
|
||||||
|
|
||||||
|
return json.Success("操作成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ping 测试主机是否可连接
|
||||||
|
func Ping(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
hostModel := new(models.Host)
|
||||||
|
err := hostModel.Find(id)
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if err != nil || hostModel.Id <= 0 {
|
||||||
|
return json.CommonFailure("主机不存在", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskReq := &rpc.TaskRequest{}
|
||||||
|
taskReq.Command = testConnectionCommand
|
||||||
|
taskReq.Timeout = testConnectionTimeout
|
||||||
|
output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("连接失败-"+err.Error()+" "+output, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("连接成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析查询参数
|
||||||
|
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||||
|
var params = models.CommonMap{}
|
||||||
|
params["Id"] = ctx.QueryInt("id")
|
||||||
|
params["Name"] = ctx.QueryTrim("name")
|
||||||
|
base.ParsePageAndPageSize(ctx, params)
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package install
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/go-macaron/binding"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/setting"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/service"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 系统安装
|
||||||
|
|
||||||
|
type InstallForm struct {
|
||||||
|
DbType string `binding:"In(mysql)"`
|
||||||
|
DbHost string `binding:"Required;MaxSize(50)"`
|
||||||
|
DbPort int `binding:"Required;Range(1,65535)"`
|
||||||
|
DbUsername string `binding:"Required;MaxSize(50)"`
|
||||||
|
DbPassword string `binding:"Required;MaxSize(30)"`
|
||||||
|
DbName string `binding:"Required;MaxSize(50)"`
|
||||||
|
DbTablePrefix string `binding:"MaxSize(20)"`
|
||||||
|
AdminUsername string `binding:"Required;MinSize(3)"`
|
||||||
|
AdminPassword string `binding:"Required;MinSize(6)"`
|
||||||
|
ConfirmAdminPassword string `binding:"Required;MinSize(6)"`
|
||||||
|
AdminEmail string `binding:"Required;Email;MaxSize(50)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||||
|
ctx.Write([]byte(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安装
|
||||||
|
func Store(ctx *macaron.Context, form InstallForm) string {
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if app.Installed {
|
||||||
|
return json.CommonFailure("系统已安装!")
|
||||||
|
}
|
||||||
|
if form.AdminPassword != form.ConfirmAdminPassword {
|
||||||
|
return json.CommonFailure("两次输入密码不匹配")
|
||||||
|
}
|
||||||
|
err := testDbConnection(form)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("数据库连接失败", err)
|
||||||
|
}
|
||||||
|
// 写入数据库配置
|
||||||
|
err = writeConfig(form)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("数据库配置写入文件失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
appConfig, err := setting.Read(app.AppConfig)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("读取应用配置失败", err)
|
||||||
|
}
|
||||||
|
app.Setting = appConfig
|
||||||
|
|
||||||
|
models.Db = models.CreateDb()
|
||||||
|
// 创建数据库表
|
||||||
|
migration := new(models.Migration)
|
||||||
|
err = migration.Install(form.DbName)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建管理员账号
|
||||||
|
err = createAdminUser(form)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("创建管理员账号失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建安装锁
|
||||||
|
err = app.CreateInstallLock()
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("创建文件安装锁失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新版本号文件
|
||||||
|
app.UpdateVersionFile()
|
||||||
|
|
||||||
|
app.Installed = true
|
||||||
|
// 初始化定时任务
|
||||||
|
service.ServiceTask.Initialize()
|
||||||
|
|
||||||
|
return json.Success("安装成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置写入文件
|
||||||
|
func writeConfig(form InstallForm) error {
|
||||||
|
dbConfig := []string{
|
||||||
|
"db.engine", form.DbType,
|
||||||
|
"db.host", form.DbHost,
|
||||||
|
"db.port", strconv.Itoa(form.DbPort),
|
||||||
|
"db.user", form.DbUsername,
|
||||||
|
"db.password", form.DbPassword,
|
||||||
|
"db.database", form.DbName,
|
||||||
|
"db.prefix", form.DbTablePrefix,
|
||||||
|
"db.charset", "utf8",
|
||||||
|
"db.max.idle.conns", "30",
|
||||||
|
"db.max.open.conns", "100",
|
||||||
|
"allow_ips", "",
|
||||||
|
"app.name", "定时任务管理系统", // 应用名称
|
||||||
|
"api.key", "",
|
||||||
|
"api.secret", "",
|
||||||
|
"enable_tls", "false",
|
||||||
|
"concurrency.queue", "500",
|
||||||
|
"auth_secret", utils.RandAuthToken(),
|
||||||
|
"ca_file", "",
|
||||||
|
"cert_file", "",
|
||||||
|
"key_file", "",
|
||||||
|
}
|
||||||
|
|
||||||
|
return setting.Write(dbConfig, app.AppConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建管理员账号
|
||||||
|
func createAdminUser(form InstallForm) error {
|
||||||
|
user := new(models.User)
|
||||||
|
user.Name = form.AdminUsername
|
||||||
|
user.Password = form.AdminPassword
|
||||||
|
user.Email = form.AdminEmail
|
||||||
|
user.IsAdmin = 1
|
||||||
|
_, err := user.Create()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试数据库连接
|
||||||
|
func testDbConnection(form InstallForm) error {
|
||||||
|
var s setting.Setting
|
||||||
|
s.Db.Engine = form.DbType
|
||||||
|
s.Db.Host = form.DbHost
|
||||||
|
s.Db.Port = form.DbPort
|
||||||
|
s.Db.User = form.DbUsername
|
||||||
|
s.Db.Password = form.DbPassword
|
||||||
|
s.Db.Charset = "utf8"
|
||||||
|
db, err := models.CreateTmpDb(&s)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer db.Close()
|
||||||
|
err = db.Ping()
|
||||||
|
|
||||||
|
return err
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package loginlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/base"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Index(ctx *macaron.Context) string {
|
||||||
|
loginLogModel := new(models.LoginLog)
|
||||||
|
params := models.CommonMap{}
|
||||||
|
base.ParsePageAndPageSize(ctx, params)
|
||||||
|
total, err := loginLogModel.Total()
|
||||||
|
loginLogs, err := loginLogModel.List(params)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"total": total,
|
||||||
|
"data": loginLogs,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package manage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Slack(ctx *macaron.Context) string {
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
slack, err := settingModel.Slack()
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, slack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateSlack(ctx *macaron.Context) string {
|
||||||
|
url := ctx.QueryTrim("url")
|
||||||
|
template := ctx.QueryTrim("template")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
err := settingModel.UpdateSlack(url, template)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateSlackChannel(ctx *macaron.Context) string {
|
||||||
|
channel := ctx.QueryTrim("channel")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
if settingModel.IsChannelExist(channel) {
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.CommonFailure("Channel已存在")
|
||||||
|
}
|
||||||
|
_, err := settingModel.CreateChannel(channel)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSlackChannel(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
_, err := settingModel.RemoveChannel(id)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
// region 邮件
|
||||||
|
func Mail(ctx *macaron.Context) string {
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
mail, err := settingModel.Mail()
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success("", mail)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MailServerForm struct {
|
||||||
|
Host string `binding:"Required;MaxSize(100)"`
|
||||||
|
Port int `binding:"Required;Range(1-65535)"`
|
||||||
|
User string `binding:"Required;MaxSize(64);Email"`
|
||||||
|
Password string `binding:"Required;MaxSize(64)"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateMail(ctx *macaron.Context, form MailServerForm) string {
|
||||||
|
jsonByte, _ := json.Marshal(form)
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
|
||||||
|
template := ctx.QueryTrim("template")
|
||||||
|
err := settingModel.UpdateMail(string(jsonByte), template)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateMailUser(ctx *macaron.Context) string {
|
||||||
|
username := ctx.QueryTrim("username")
|
||||||
|
email := ctx.QueryTrim("email")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
if username == "" || email == "" {
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.CommonFailure("用户名、邮箱均不能为空")
|
||||||
|
}
|
||||||
|
_, err := settingModel.CreateMailUser(username, email)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveMailUser(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
_, err := settingModel.RemoveMailUser(id)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WebHook(ctx *macaron.Context) string {
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
webHook, err := settingModel.Webhook()
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success("", webHook)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateWebHook(ctx *macaron.Context) string {
|
||||||
|
url := ctx.QueryTrim("url")
|
||||||
|
template := ctx.QueryTrim("template")
|
||||||
|
settingModel := new(models.Setting)
|
||||||
|
err := settingModel.UpdateWebHook(url, template)
|
||||||
|
|
||||||
|
return utils.JsonResponseByErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
@@ -0,0 +1,314 @@
|
|||||||
|
package routers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-macaron/binding"
|
||||||
|
"github.com/go-macaron/gzip"
|
||||||
|
"github.com/go-macaron/toolbox"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/host"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/install"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/loginlog"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/manage"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/task"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/tasklog"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/user"
|
||||||
|
"github.com/rakyll/statik/fs"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
|
||||||
|
_ "github.com/ouqiang/gocron/internal/statik"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
urlPrefix = "/api"
|
||||||
|
staticDir = "public"
|
||||||
|
)
|
||||||
|
|
||||||
|
var statikFS http.FileSystem
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var err error
|
||||||
|
statikFS, err = fs.New()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 路由注册
|
||||||
|
func Register(m *macaron.Macaron) {
|
||||||
|
m.SetURLPrefix(urlPrefix)
|
||||||
|
// 所有GET方法,自动注册HEAD方法
|
||||||
|
m.SetAutoHead(true)
|
||||||
|
m.Get("/", func(ctx *macaron.Context) {
|
||||||
|
file, err := statikFS.Open("/index.html")
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("读取首页文件失败: %s", err)
|
||||||
|
ctx.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.Copy(ctx.Resp, file)
|
||||||
|
|
||||||
|
})
|
||||||
|
// 系统安装
|
||||||
|
m.Group("/install", func() {
|
||||||
|
m.Post("/store", binding.Bind(install.InstallForm{}), install.Store)
|
||||||
|
m.Get("/status", func(ctx *macaron.Context) string {
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
return jsonResp.Success("", app.Installed)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 用户
|
||||||
|
m.Group("/user", func() {
|
||||||
|
m.Get("", user.Index)
|
||||||
|
m.Get("/:id", user.Detail)
|
||||||
|
m.Post("/store", binding.Bind(user.UserForm{}), user.Store)
|
||||||
|
m.Post("/remove/:id", user.Remove)
|
||||||
|
m.Post("/login", user.ValidateLogin)
|
||||||
|
m.Post("/enable/:id", user.Enable)
|
||||||
|
m.Post("/disable/:id", user.Disable)
|
||||||
|
m.Post("/editMyPassword", user.UpdateMyPassword)
|
||||||
|
m.Post("/editPassword/:id", user.UpdatePassword)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 定时任务
|
||||||
|
m.Group("/task", func() {
|
||||||
|
m.Post("/store", binding.Bind(task.TaskForm{}), task.Store)
|
||||||
|
m.Get("/:id", task.Detail)
|
||||||
|
m.Get("", task.Index)
|
||||||
|
m.Get("/log", tasklog.Index)
|
||||||
|
m.Post("/log/clear", tasklog.Clear)
|
||||||
|
m.Post("/log/stop", tasklog.Stop)
|
||||||
|
m.Post("/remove/:id", task.Remove)
|
||||||
|
m.Post("/enable/:id", task.Enable)
|
||||||
|
m.Post("/disable/:id", task.Disable)
|
||||||
|
m.Get("/run/:id", task.Run)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 主机
|
||||||
|
m.Group("/host", func() {
|
||||||
|
m.Get("/:id", host.Detail)
|
||||||
|
m.Post("/store", binding.Bind(host.HostForm{}), host.Store)
|
||||||
|
m.Get("", host.Index)
|
||||||
|
m.Get("/ping/:id", host.Ping)
|
||||||
|
m.Post("/remove/:id", host.Remove)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 管理
|
||||||
|
m.Group("/system", func() {
|
||||||
|
m.Group("/slack", func() {
|
||||||
|
m.Get("", manage.Slack)
|
||||||
|
m.Post("/update", manage.UpdateSlack)
|
||||||
|
m.Post("/channel", manage.CreateSlackChannel)
|
||||||
|
m.Post("/channel/remove/:id", manage.RemoveSlackChannel)
|
||||||
|
})
|
||||||
|
m.Group("/mail", func() {
|
||||||
|
m.Get("", manage.Mail)
|
||||||
|
m.Post("/update", binding.Bind(manage.MailServerForm{}), manage.UpdateMail)
|
||||||
|
m.Post("/user", manage.CreateMailUser)
|
||||||
|
m.Post("/user/remove/:id", manage.RemoveMailUser)
|
||||||
|
})
|
||||||
|
m.Group("/webhook", func() {
|
||||||
|
m.Get("", manage.WebHook)
|
||||||
|
m.Post("/update", manage.UpdateWebHook)
|
||||||
|
})
|
||||||
|
m.Get("/login-log", loginlog.Index)
|
||||||
|
})
|
||||||
|
|
||||||
|
// API
|
||||||
|
m.Group("/v1", func() {
|
||||||
|
m.Post("/tasklog/remove/:id", tasklog.Remove)
|
||||||
|
m.Post("/task/enable/:id", task.Enable)
|
||||||
|
m.Post("/task/disable/:id", task.Disable)
|
||||||
|
}, apiAuth)
|
||||||
|
|
||||||
|
// 404错误
|
||||||
|
m.NotFound(func(ctx *macaron.Context) string {
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Failure(utils.NotFound, "您访问的页面不存在")
|
||||||
|
})
|
||||||
|
// 50x错误
|
||||||
|
m.InternalServerError(func(ctx *macaron.Context) string {
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Failure(utils.ServerError, "服务器内部错误, 请稍后再试")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中间件注册
|
||||||
|
func RegisterMiddleware(m *macaron.Macaron) {
|
||||||
|
m.Use(macaron.Logger())
|
||||||
|
m.Use(macaron.Recovery())
|
||||||
|
if macaron.Env != macaron.DEV {
|
||||||
|
m.Use(gzip.Gziper())
|
||||||
|
}
|
||||||
|
m.Use(
|
||||||
|
macaron.Static(
|
||||||
|
"",
|
||||||
|
macaron.StaticOptions{
|
||||||
|
Prefix: staticDir,
|
||||||
|
FileSystem: statikFS,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if macaron.Env == macaron.DEV {
|
||||||
|
m.Use(toolbox.Toolboxer(m))
|
||||||
|
}
|
||||||
|
m.Use(macaron.Renderer())
|
||||||
|
m.Use(checkAppInstall)
|
||||||
|
m.Use(ipAuth)
|
||||||
|
m.Use(userAuth)
|
||||||
|
m.Use(urlAuth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// region 自定义中间件
|
||||||
|
|
||||||
|
/** 检测应用是否已安装 **/
|
||||||
|
func checkAppInstall(ctx *macaron.Context) {
|
||||||
|
if app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(ctx.Req.URL.Path, "/install") || ctx.Req.URL.Path == "/" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
data := jsonResp.Failure(utils.AppNotInstall, "应用未安装")
|
||||||
|
ctx.Write([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// IP验证, 通过反向代理访问gocron,需设置Header X-Real-IP才能获取到客户端真实IP
|
||||||
|
func ipAuth(ctx *macaron.Context) {
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
allowIpsStr := app.Setting.AllowIps
|
||||||
|
if allowIpsStr == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
clientIp := ctx.RemoteAddr()
|
||||||
|
allowIps := strings.Split(allowIpsStr, ",")
|
||||||
|
if utils.InStringSlice(allowIps, clientIp) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Warnf("非法IP访问-%s", clientIp)
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
data := jsonResp.Failure(utils.UnauthorizedError, "您无权限访问")
|
||||||
|
|
||||||
|
ctx.Write([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户认证
|
||||||
|
func userAuth(ctx *macaron.Context) {
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user.RestoreToken(ctx)
|
||||||
|
if user.IsLogin(ctx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uri := strings.TrimRight(ctx.Req.URL.Path, "/")
|
||||||
|
if strings.HasPrefix(uri, "/v1") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
excludePaths := []string{"", "/user/login", "/install/status"}
|
||||||
|
for _, path := range excludePaths {
|
||||||
|
if uri == path {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
data := jsonResp.Failure(utils.AuthError, "认证失败")
|
||||||
|
ctx.Write([]byte(data))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL权限验证
|
||||||
|
func urlAuth(ctx *macaron.Context) {
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if user.IsAdmin(ctx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
uri := strings.TrimRight(ctx.Req.URL.Path, "/")
|
||||||
|
if strings.HasPrefix(uri, "/v1") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 普通用户允许访问的URL地址
|
||||||
|
allowPaths := []string{
|
||||||
|
"",
|
||||||
|
"/install/status",
|
||||||
|
"/task",
|
||||||
|
"/task/log",
|
||||||
|
"/host",
|
||||||
|
"/user/login",
|
||||||
|
"/user/editMyPassword",
|
||||||
|
}
|
||||||
|
for _, path := range allowPaths {
|
||||||
|
if path == uri {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
data := jsonResp.Failure(utils.UnauthorizedError, "您无权限访问")
|
||||||
|
ctx.Write([]byte(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** API接口签名验证 **/
|
||||||
|
func apiAuth(ctx *macaron.Context) {
|
||||||
|
if !app.Installed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !app.Setting.ApiSignEnable {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apiKey := strings.TrimSpace(app.Setting.ApiKey)
|
||||||
|
apiSecret := strings.TrimSpace(app.Setting.ApiSecret)
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if apiKey == "" || apiSecret == "" {
|
||||||
|
msg := json.CommonFailure("使用API前, 请先配置密钥")
|
||||||
|
ctx.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
currentTimestamp := time.Now().Unix()
|
||||||
|
time := ctx.QueryInt64("time")
|
||||||
|
if time <= 0 {
|
||||||
|
msg := json.CommonFailure("参数time不能为空")
|
||||||
|
ctx.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if time < (currentTimestamp - 1800) {
|
||||||
|
msg := json.CommonFailure("time无效")
|
||||||
|
ctx.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sign := ctx.QueryTrim("sign")
|
||||||
|
if sign == "" {
|
||||||
|
msg := json.CommonFailure("参数sign不能为空")
|
||||||
|
ctx.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret
|
||||||
|
realSign := utils.Md5(raw)
|
||||||
|
if sign != realSign {
|
||||||
|
msg := json.CommonFailure("签名验证失败")
|
||||||
|
ctx.Write([]byte(msg))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// endregion
|
||||||
@@ -0,0 +1,293 @@
|
|||||||
|
package task
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-macaron/binding"
|
||||||
|
"github.com/jakecoffman/cron"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/base"
|
||||||
|
"github.com/ouqiang/gocron/internal/service"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskForm struct {
|
||||||
|
Id int
|
||||||
|
Level models.TaskLevel `binding:"Required;In(1,2)"`
|
||||||
|
DependencyStatus models.TaskDependencyStatus
|
||||||
|
DependencyTaskId string
|
||||||
|
Name string `binding:"Required;MaxSize(32)"`
|
||||||
|
Spec string
|
||||||
|
Protocol models.TaskProtocol `binding:"In(1,2)"`
|
||||||
|
Command string `binding:"Required;MaxSize(256)"`
|
||||||
|
HttpMethod models.TaskHTTPMethod `binding:"In(1,2)"`
|
||||||
|
Timeout int `binding:"Range(0,86400)"`
|
||||||
|
Multi int8 `binding:"In(1,2)"`
|
||||||
|
RetryTimes int8
|
||||||
|
RetryInterval int16
|
||||||
|
HostId string
|
||||||
|
Tag string
|
||||||
|
Remark string
|
||||||
|
NotifyStatus int8 `binding:"In(1,2,3,4)"`
|
||||||
|
NotifyType int8 `binding:"In(1,2,3,4)"`
|
||||||
|
NotifyReceiverId string
|
||||||
|
NotifyKeyword string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f TaskForm) Error(ctx *macaron.Context, errs binding.Errors) {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
content := json.CommonFailure("表单验证失败, 请检测输入")
|
||||||
|
|
||||||
|
ctx.Resp.Write([]byte(content))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首页
|
||||||
|
func Index(ctx *macaron.Context) string {
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
queryParams := parseQueryParams(ctx)
|
||||||
|
total, err := taskModel.Total(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
tasks, err := taskModel.List(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
for i, item := range tasks {
|
||||||
|
tasks[i].NextRunTime = service.ServiceTask.NextRunTime(item)
|
||||||
|
}
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"total": total,
|
||||||
|
"data": tasks,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 任务详情
|
||||||
|
func Detail(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
task, err := taskModel.Detail(id)
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if err != nil || task.Id == 0 {
|
||||||
|
logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d", id)
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存任务 todo 拆分为多个方法
|
||||||
|
func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
taskModel := models.Task{}
|
||||||
|
var id = form.Id
|
||||||
|
nameExists, err := taskModel.NameExist(form.Name, form.Id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
if nameExists {
|
||||||
|
return json.CommonFailure("任务名称已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Protocol == models.TaskRPC && form.HostId == "" {
|
||||||
|
return json.CommonFailure("请选择主机名")
|
||||||
|
}
|
||||||
|
|
||||||
|
taskModel.Name = form.Name
|
||||||
|
taskModel.Protocol = form.Protocol
|
||||||
|
taskModel.Command = strings.TrimSpace(form.Command)
|
||||||
|
taskModel.Timeout = form.Timeout
|
||||||
|
taskModel.Tag = form.Tag
|
||||||
|
taskModel.Remark = form.Remark
|
||||||
|
taskModel.Multi = form.Multi
|
||||||
|
taskModel.RetryTimes = form.RetryTimes
|
||||||
|
taskModel.RetryInterval = form.RetryInterval
|
||||||
|
if taskModel.Multi != 1 {
|
||||||
|
taskModel.Multi = 0
|
||||||
|
}
|
||||||
|
taskModel.NotifyStatus = form.NotifyStatus - 1
|
||||||
|
taskModel.NotifyType = form.NotifyType - 1
|
||||||
|
taskModel.NotifyReceiverId = form.NotifyReceiverId
|
||||||
|
taskModel.NotifyKeyword = form.NotifyKeyword
|
||||||
|
taskModel.Spec = form.Spec
|
||||||
|
taskModel.Level = form.Level
|
||||||
|
taskModel.DependencyStatus = form.DependencyStatus
|
||||||
|
taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId)
|
||||||
|
if taskModel.NotifyStatus > 0 && taskModel.NotifyType != 3 && taskModel.NotifyReceiverId == "" {
|
||||||
|
return json.CommonFailure("至少选择一个通知接收者")
|
||||||
|
}
|
||||||
|
taskModel.HttpMethod = form.HttpMethod
|
||||||
|
if taskModel.Protocol == models.TaskHTTP {
|
||||||
|
command := strings.ToLower(taskModel.Command)
|
||||||
|
if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") {
|
||||||
|
return json.CommonFailure("请输入正确的URL地址")
|
||||||
|
}
|
||||||
|
if taskModel.Timeout > 300 {
|
||||||
|
return json.CommonFailure("HTTP任务超时时间不能超过300秒")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 {
|
||||||
|
return json.CommonFailure("任务重试次数取值0-10")
|
||||||
|
}
|
||||||
|
|
||||||
|
if taskModel.RetryInterval > 3600 || taskModel.RetryInterval < 0 {
|
||||||
|
return json.CommonFailure("任务重试间隔时间取值0-3600")
|
||||||
|
}
|
||||||
|
|
||||||
|
if taskModel.DependencyStatus != models.TaskDependencyStatusStrong &&
|
||||||
|
taskModel.DependencyStatus != models.TaskDependencyStatusWeak {
|
||||||
|
return json.CommonFailure("请选择依赖关系")
|
||||||
|
}
|
||||||
|
|
||||||
|
if taskModel.Level == models.TaskLevelParent {
|
||||||
|
_, err = cron.Parse(form.Spec)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("crontab表达式解析失败", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
taskModel.DependencyTaskId = ""
|
||||||
|
taskModel.Spec = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if id > 0 && taskModel.DependencyTaskId != "" {
|
||||||
|
dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",")
|
||||||
|
if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) {
|
||||||
|
return json.CommonFailure("不允许设置当前任务为子任务")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if id == 0 {
|
||||||
|
// 任务添加后开始调度执行
|
||||||
|
taskModel.Status = models.Running
|
||||||
|
id, err = taskModel.Create()
|
||||||
|
} else {
|
||||||
|
_, err = taskModel.UpdateBean(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("保存失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskHostModel := new(models.TaskHost)
|
||||||
|
if form.Protocol == models.TaskRPC {
|
||||||
|
hostIdStrList := strings.Split(form.HostId, ",")
|
||||||
|
hostIds := make([]int, len(hostIdStrList))
|
||||||
|
for i, hostIdStr := range hostIdStrList {
|
||||||
|
hostIds[i], _ = strconv.Atoi(hostIdStr)
|
||||||
|
}
|
||||||
|
taskHostModel.Add(id, hostIds)
|
||||||
|
} else {
|
||||||
|
taskHostModel.Remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _ := taskModel.GetStatus(id)
|
||||||
|
if status == models.Enabled && taskModel.Level == models.TaskLevelParent {
|
||||||
|
addTaskToTimer(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("保存成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除任务
|
||||||
|
func Remove(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
_, err := taskModel.Delete(id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
taskHostModel := new(models.TaskHost)
|
||||||
|
taskHostModel.Remove(id)
|
||||||
|
|
||||||
|
service.ServiceTask.Remove(id)
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 激活任务
|
||||||
|
func Enable(ctx *macaron.Context) string {
|
||||||
|
return changeStatus(ctx, models.Enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 暂停任务
|
||||||
|
func Disable(ctx *macaron.Context) string {
|
||||||
|
return changeStatus(ctx, models.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手动运行任务
|
||||||
|
func Run(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
task, err := taskModel.Detail(id)
|
||||||
|
if err != nil || task.Id <= 0 {
|
||||||
|
return json.CommonFailure("获取任务详情失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
task.Spec = "手动运行"
|
||||||
|
service.ServiceTask.Run(task)
|
||||||
|
|
||||||
|
return json.Success("任务已开始运行, 请到任务日志中查看结果", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变任务状态
|
||||||
|
func changeStatus(ctx *macaron.Context, status models.Status) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
_, err := taskModel.Update(id, models.CommonMap{
|
||||||
|
"Status": status,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status == models.Enabled {
|
||||||
|
addTaskToTimer(id)
|
||||||
|
} else {
|
||||||
|
service.ServiceTask.Remove(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加任务到定时器
|
||||||
|
func addTaskToTimer(id int) {
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
task, err := taskModel.Detail(id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
service.ServiceTask.RemoveAndAdd(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析查询参数
|
||||||
|
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||||
|
var params models.CommonMap = models.CommonMap{}
|
||||||
|
params["Id"] = ctx.QueryInt("id")
|
||||||
|
params["HostId"] = ctx.QueryInt("host_id")
|
||||||
|
params["Name"] = ctx.QueryTrim("name")
|
||||||
|
params["Protocol"] = ctx.QueryInt("protocol")
|
||||||
|
params["Tag"] = ctx.QueryTrim("tag")
|
||||||
|
status := ctx.QueryInt("status")
|
||||||
|
if status >= 0 {
|
||||||
|
status -= 1
|
||||||
|
}
|
||||||
|
params["Status"] = status
|
||||||
|
base.ParsePageAndPageSize(ctx, params)
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package tasklog
|
||||||
|
|
||||||
|
// 任务日志
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/base"
|
||||||
|
"github.com/ouqiang/gocron/internal/service"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Index(ctx *macaron.Context) string {
|
||||||
|
logModel := new(models.TaskLog)
|
||||||
|
queryParams := parseQueryParams(ctx)
|
||||||
|
total, err := logModel.Total(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
logs, err := logModel.List(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"total": total,
|
||||||
|
"data": logs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空日志
|
||||||
|
func Clear(ctx *macaron.Context) string {
|
||||||
|
taskLogModel := new(models.TaskLog)
|
||||||
|
_, err := taskLogModel.Clear()
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止运行中的任务
|
||||||
|
func Stop(ctx *macaron.Context) string {
|
||||||
|
id := ctx.QueryInt64("id")
|
||||||
|
taskId := ctx.QueryInt("task_id")
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
task, err := taskModel.Detail(taskId)
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("获取任务信息失败#"+err.Error(), err)
|
||||||
|
}
|
||||||
|
if task.Protocol != models.TaskRPC {
|
||||||
|
return json.CommonFailure("仅支持SHELL任务手动停止")
|
||||||
|
}
|
||||||
|
if len(task.Hosts) == 0 {
|
||||||
|
return json.CommonFailure("任务节点列表为空")
|
||||||
|
}
|
||||||
|
for _, host := range task.Hosts {
|
||||||
|
service.ServiceTask.Stop(host.Name, host.Port, id)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("已执行停止操作, 请等待任务退出", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除N个月前的日志
|
||||||
|
func Remove(ctx *macaron.Context) string {
|
||||||
|
month := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if month < 1 || month > 12 {
|
||||||
|
return json.CommonFailure("参数取值范围1-12")
|
||||||
|
}
|
||||||
|
taskLogModel := new(models.TaskLog)
|
||||||
|
_, err := taskLogModel.Remove(month)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("删除失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("删除成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析查询参数
|
||||||
|
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||||
|
var params models.CommonMap = models.CommonMap{}
|
||||||
|
params["TaskId"] = ctx.QueryInt("task_id")
|
||||||
|
params["Protocol"] = ctx.QueryInt("protocol")
|
||||||
|
status := ctx.QueryInt("status")
|
||||||
|
if status >= 0 {
|
||||||
|
status -= 1
|
||||||
|
}
|
||||||
|
params["Status"] = status
|
||||||
|
base.ParsePageAndPageSize(ctx, params)
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/utils"
|
||||||
|
"github.com/ouqiang/gocron/internal/routers/base"
|
||||||
|
"gopkg.in/macaron.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
const tokenDuration = 4 * time.Hour
|
||||||
|
|
||||||
|
// UserForm 用户表单
|
||||||
|
type UserForm struct {
|
||||||
|
Id int
|
||||||
|
Name string `binding:"Required;MaxSize(32)"` // 用户名
|
||||||
|
Password string // 密码
|
||||||
|
ConfirmPassword string // 确认密码
|
||||||
|
Email string `binding:"Required;MaxSize(50)"` // 邮箱
|
||||||
|
IsAdmin int8 // 是否是管理员 1:管理员 0:普通用户
|
||||||
|
Status models.Status
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index 用户列表页
|
||||||
|
func Index(ctx *macaron.Context) string {
|
||||||
|
queryParams := parseQueryParams(ctx)
|
||||||
|
userModel := new(models.User)
|
||||||
|
users, err := userModel.List(queryParams)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
total, err := userModel.Total()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"total": total,
|
||||||
|
"data": users,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析查询参数
|
||||||
|
func parseQueryParams(ctx *macaron.Context) models.CommonMap {
|
||||||
|
params := models.CommonMap{}
|
||||||
|
base.ParsePageAndPageSize(ctx, params)
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detail 用户详情
|
||||||
|
func Detail(ctx *macaron.Context) string {
|
||||||
|
userModel := new(models.User)
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
err := userModel.Find(id)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
jsonResp := utils.JsonResponse{}
|
||||||
|
if userModel.Id == 0 {
|
||||||
|
return jsonResp.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return jsonResp.Success(utils.SuccessContent, userModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存任务
|
||||||
|
func Store(ctx *macaron.Context, form UserForm) string {
|
||||||
|
form.Name = strings.TrimSpace(form.Name)
|
||||||
|
form.Email = strings.TrimSpace(form.Email)
|
||||||
|
form.Password = strings.TrimSpace(form.Password)
|
||||||
|
form.ConfirmPassword = strings.TrimSpace(form.ConfirmPassword)
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
userModel := models.User{}
|
||||||
|
nameExists, err := userModel.UsernameExists(form.Name, form.Id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
if nameExists > 0 {
|
||||||
|
return json.CommonFailure("用户名已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
emailExists, err := userModel.EmailExists(form.Email, form.Id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
if emailExists > 0 {
|
||||||
|
return json.CommonFailure("邮箱已存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Id == 0 {
|
||||||
|
if form.Password == "" {
|
||||||
|
return json.CommonFailure("请输入密码")
|
||||||
|
}
|
||||||
|
if form.ConfirmPassword == "" {
|
||||||
|
return json.CommonFailure("请再次输入密码")
|
||||||
|
}
|
||||||
|
if form.Password != form.ConfirmPassword {
|
||||||
|
return json.CommonFailure("两次密码输入不一致")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userModel.Name = form.Name
|
||||||
|
userModel.Email = form.Email
|
||||||
|
userModel.Password = form.Password
|
||||||
|
userModel.IsAdmin = form.IsAdmin
|
||||||
|
userModel.Status = form.Status
|
||||||
|
|
||||||
|
if form.Id == 0 {
|
||||||
|
_, err = userModel.Create()
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("添加失败", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err = userModel.Update(form.Id, models.CommonMap{
|
||||||
|
"name": form.Name,
|
||||||
|
"email": form.Email,
|
||||||
|
"status": form.Status,
|
||||||
|
"is_admin": form.IsAdmin,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("修改失败", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("保存成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除用户
|
||||||
|
func Remove(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
|
||||||
|
userModel := new(models.User)
|
||||||
|
_, err := userModel.Delete(id)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 激活用户
|
||||||
|
func Enable(ctx *macaron.Context) string {
|
||||||
|
return changeStatus(ctx, models.Enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 禁用用户
|
||||||
|
func Disable(ctx *macaron.Context) string {
|
||||||
|
return changeStatus(ctx, models.Disabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变任务状态
|
||||||
|
func changeStatus(ctx *macaron.Context, status models.Status) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
userModel := new(models.User)
|
||||||
|
_, err := userModel.Update(id, models.CommonMap{
|
||||||
|
"Status": status,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure(utils.FailureContent, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdatePassword 更新密码
|
||||||
|
func UpdatePassword(ctx *macaron.Context) string {
|
||||||
|
id := ctx.ParamsInt(":id")
|
||||||
|
newPassword := ctx.QueryTrim("new_password")
|
||||||
|
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if newPassword == "" || confirmNewPassword == "" {
|
||||||
|
return json.CommonFailure("请输入密码")
|
||||||
|
}
|
||||||
|
if newPassword != confirmNewPassword {
|
||||||
|
return json.CommonFailure("两次输入密码不一致")
|
||||||
|
}
|
||||||
|
userModel := new(models.User)
|
||||||
|
_, err := userModel.UpdatePassword(id, newPassword)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("修改失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("修改成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateMyPassword 更新我的密码
|
||||||
|
func UpdateMyPassword(ctx *macaron.Context) string {
|
||||||
|
oldPassword := ctx.QueryTrim("old_password")
|
||||||
|
newPassword := ctx.QueryTrim("new_password")
|
||||||
|
confirmNewPassword := ctx.QueryTrim("confirm_new_password")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if oldPassword == "" || newPassword == "" || confirmNewPassword == "" {
|
||||||
|
return json.CommonFailure("原密码和新密码均不能为空")
|
||||||
|
}
|
||||||
|
if newPassword != confirmNewPassword {
|
||||||
|
return json.CommonFailure("两次输入密码不一致")
|
||||||
|
}
|
||||||
|
if oldPassword == newPassword {
|
||||||
|
return json.CommonFailure("原密码与新密码不能相同")
|
||||||
|
}
|
||||||
|
userModel := new(models.User)
|
||||||
|
if !userModel.Match(Username(ctx), oldPassword) {
|
||||||
|
return json.CommonFailure("原密码输入错误")
|
||||||
|
}
|
||||||
|
_, err := userModel.UpdatePassword(Uid(ctx), newPassword)
|
||||||
|
if err != nil {
|
||||||
|
return json.CommonFailure("修改失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success("修改成功", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateLogin 验证用户登录
|
||||||
|
func ValidateLogin(ctx *macaron.Context) string {
|
||||||
|
username := ctx.QueryTrim("username")
|
||||||
|
password := ctx.QueryTrim("password")
|
||||||
|
json := utils.JsonResponse{}
|
||||||
|
if username == "" || password == "" {
|
||||||
|
return json.CommonFailure("用户名、密码不能为空")
|
||||||
|
}
|
||||||
|
userModel := new(models.User)
|
||||||
|
if !userModel.Match(username, password) {
|
||||||
|
return json.CommonFailure("用户名或密码错误")
|
||||||
|
}
|
||||||
|
loginLogModel := new(models.LoginLog)
|
||||||
|
loginLogModel.Username = userModel.Name
|
||||||
|
loginLogModel.Ip = ctx.RemoteAddr()
|
||||||
|
_, err := loginLogModel.Create()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("记录用户登录日志失败", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := generateToken(userModel)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("生成jwt失败: %s", err)
|
||||||
|
return json.Failure(utils.AuthError, "认证失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Success(utils.SuccessContent, map[string]interface{}{
|
||||||
|
"token": token,
|
||||||
|
"uid": userModel.Id,
|
||||||
|
"username": userModel.Name,
|
||||||
|
"is_admin": userModel.IsAdmin,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Username 获取session中的用户名
|
||||||
|
func Username(ctx *macaron.Context) string {
|
||||||
|
usernameInterface, ok := ctx.Data["username"]
|
||||||
|
if !ok {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if username, ok := usernameInterface.(string); ok {
|
||||||
|
return username
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uid 获取session中的Uid
|
||||||
|
func Uid(ctx *macaron.Context) int {
|
||||||
|
uidInterface, ok := ctx.Data["uid"]
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if uid, ok := uidInterface.(int); ok {
|
||||||
|
return uid
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLogin 判断用户是否已登录
|
||||||
|
func IsLogin(ctx *macaron.Context) bool {
|
||||||
|
return Uid(ctx) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAdmin 判断当前用户是否是管理员
|
||||||
|
func IsAdmin(ctx *macaron.Context) bool {
|
||||||
|
isAdmin, ok := ctx.Data["is_admin"]
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if v, ok := isAdmin.(int); ok {
|
||||||
|
return v > 0
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成jwt
|
||||||
|
func generateToken(user *models.User) (string, error) {
|
||||||
|
token := jwt.New(jwt.SigningMethodHS256)
|
||||||
|
claims := make(jwt.MapClaims)
|
||||||
|
claims["exp"] = time.Now().Add(tokenDuration).Unix()
|
||||||
|
claims["uid"] = user.Id
|
||||||
|
claims["iat"] = time.Now().Unix()
|
||||||
|
claims["issuer"] = "gocron"
|
||||||
|
claims["username"] = user.Name
|
||||||
|
claims["is_admin"] = user.IsAdmin
|
||||||
|
token.Claims = claims
|
||||||
|
|
||||||
|
return token.SignedString([]byte(app.Setting.AuthSecret))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 还原jwt
|
||||||
|
func RestoreToken(ctx *macaron.Context) error {
|
||||||
|
authToken := ctx.Req.Header.Get("Auth-Token")
|
||||||
|
if authToken == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
token, err := jwt.Parse(authToken, func(*jwt.Token) (interface{}, error) {
|
||||||
|
return []byte(app.Setting.AuthSecret), nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, ok := token.Claims.(jwt.MapClaims)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid claims")
|
||||||
|
}
|
||||||
|
ctx.Data["uid"] = int(claims["uid"].(float64))
|
||||||
|
ctx.Data["username"] = claims["username"]
|
||||||
|
ctx.Data["is_admin"] = int(claims["is_admin"].(float64))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,493 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jakecoffman/cron"
|
||||||
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/httpclient"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/logger"
|
||||||
|
"github.com/ouqiang/gocron/internal/modules/notify"
|
||||||
|
rpcClient "github.com/ouqiang/gocron/internal/modules/rpc/client"
|
||||||
|
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ServiceTask Task
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// 定时任务调度管理器
|
||||||
|
serviceCron *cron.Cron
|
||||||
|
|
||||||
|
// 同一任务是否有实例处于运行中
|
||||||
|
runInstance Instance
|
||||||
|
|
||||||
|
// 任务计数-正在运行的任务
|
||||||
|
taskCount TaskCount
|
||||||
|
|
||||||
|
// 并发队列, 限制同时运行的任务数量
|
||||||
|
concurrencyQueue ConcurrencyQueue
|
||||||
|
)
|
||||||
|
|
||||||
|
// 并发队列
|
||||||
|
type ConcurrencyQueue struct {
|
||||||
|
queue chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cq *ConcurrencyQueue) Add() {
|
||||||
|
cq.queue <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cq *ConcurrencyQueue) Done() {
|
||||||
|
<-cq.queue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务计数
|
||||||
|
type TaskCount struct {
|
||||||
|
wg sync.WaitGroup
|
||||||
|
exit chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TaskCount) Add() {
|
||||||
|
tc.wg.Add(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TaskCount) Done() {
|
||||||
|
tc.wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TaskCount) Exit() {
|
||||||
|
tc.wg.Done()
|
||||||
|
<-tc.exit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tc *TaskCount) Wait() {
|
||||||
|
tc.Add()
|
||||||
|
tc.wg.Wait()
|
||||||
|
close(tc.exit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务ID作为Key
|
||||||
|
type Instance struct {
|
||||||
|
m sync.Map
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否有任务处于运行中
|
||||||
|
func (i *Instance) has(key int) bool {
|
||||||
|
_, ok := i.m.Load(key)
|
||||||
|
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) add(key int) {
|
||||||
|
i.m.Store(key, struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Instance) done(key int) {
|
||||||
|
i.m.Delete(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Task struct{}
|
||||||
|
|
||||||
|
type TaskResult struct {
|
||||||
|
Result string
|
||||||
|
Err error
|
||||||
|
RetryTimes int8
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行
|
||||||
|
func (task Task) Initialize() {
|
||||||
|
serviceCron = cron.New()
|
||||||
|
serviceCron.Start()
|
||||||
|
concurrencyQueue = ConcurrencyQueue{queue: make(chan struct{}, app.Setting.ConcurrencyQueue)}
|
||||||
|
taskCount = TaskCount{sync.WaitGroup{}, make(chan struct{})}
|
||||||
|
go taskCount.Wait()
|
||||||
|
|
||||||
|
logger.Info("开始初始化定时任务")
|
||||||
|
taskModel := new(models.Task)
|
||||||
|
taskNum := 0
|
||||||
|
page := 1
|
||||||
|
pageSize := 1000
|
||||||
|
maxPage := 1000
|
||||||
|
for page < maxPage {
|
||||||
|
taskList, err := taskModel.ActiveList(page, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatalf("定时任务初始化#获取任务列表错误: %s", err)
|
||||||
|
}
|
||||||
|
if len(taskList) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, item := range taskList {
|
||||||
|
task.Add(item)
|
||||||
|
taskNum++
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
logger.Infof("定时任务初始化完成, 共%d个定时任务添加到调度器", taskNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量添加任务
|
||||||
|
func (task Task) BatchAdd(tasks []models.Task) {
|
||||||
|
for _, item := range tasks {
|
||||||
|
task.RemoveAndAdd(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除任务后添加
|
||||||
|
func (task Task) RemoveAndAdd(taskModel models.Task) {
|
||||||
|
task.Remove(taskModel.Id)
|
||||||
|
task.Add(taskModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加任务
|
||||||
|
func (task Task) Add(taskModel models.Task) {
|
||||||
|
if taskModel.Level == models.TaskLevelChild {
|
||||||
|
logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
taskFunc := createJob(taskModel)
|
||||||
|
if taskFunc == nil {
|
||||||
|
logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cronName := strconv.Itoa(taskModel.Id)
|
||||||
|
err := serviceCron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("添加任务到调度器失败#", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task Task) NextRunTime(taskModel models.Task) time.Time {
|
||||||
|
if taskModel.Level != models.TaskLevelParent ||
|
||||||
|
taskModel.Status != models.Enabled {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
entries := serviceCron.Entries()
|
||||||
|
taskName := strconv.Itoa(taskModel.Id)
|
||||||
|
for _, item := range entries {
|
||||||
|
if item.Name == taskName {
|
||||||
|
return item.Next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止运行中的任务
|
||||||
|
func (task Task) Stop(ip string, port int, id int64) {
|
||||||
|
rpcClient.Stop(ip, port, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (task Task) Remove(id int) {
|
||||||
|
serviceCron.RemoveJob(strconv.Itoa(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待所有任务结束后退出
|
||||||
|
func (task Task) WaitAndExit() {
|
||||||
|
serviceCron.Stop()
|
||||||
|
taskCount.Exit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接运行任务
|
||||||
|
func (task Task) Run(taskModel models.Task) {
|
||||||
|
go createJob(taskModel)()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Run(taskModel models.Task, taskUniqueId int64) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP任务
|
||||||
|
type HTTPHandler struct{}
|
||||||
|
|
||||||
|
// http任务执行时间不超过300秒
|
||||||
|
const HttpExecTimeout = 300
|
||||||
|
|
||||||
|
func (h *HTTPHandler) Run(taskModel models.Task, taskUniqueId int64) (result string, err error) {
|
||||||
|
if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout {
|
||||||
|
taskModel.Timeout = HttpExecTimeout
|
||||||
|
}
|
||||||
|
var resp httpclient.ResponseWrapper
|
||||||
|
if taskModel.HttpMethod == models.TaskHTTPMethodGet {
|
||||||
|
resp = httpclient.Get(taskModel.Command, taskModel.Timeout)
|
||||||
|
} else {
|
||||||
|
urlFields := strings.Split(taskModel.Command, "?")
|
||||||
|
taskModel.Command = urlFields[0]
|
||||||
|
var params string
|
||||||
|
if len(urlFields) >= 2 {
|
||||||
|
params = urlFields[1]
|
||||||
|
}
|
||||||
|
resp = httpclient.PostParams(taskModel.Command, params, taskModel.Timeout)
|
||||||
|
}
|
||||||
|
// 返回状态码非200,均为失败
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode))
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp.Body, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC调用执行任务
|
||||||
|
type RPCHandler struct{}
|
||||||
|
|
||||||
|
func (h *RPCHandler) Run(taskModel models.Task, taskUniqueId int64) (result string, err error) {
|
||||||
|
taskRequest := new(pb.TaskRequest)
|
||||||
|
taskRequest.Timeout = int32(taskModel.Timeout)
|
||||||
|
taskRequest.Command = taskModel.Command
|
||||||
|
taskRequest.Id = taskUniqueId
|
||||||
|
resultChan := make(chan TaskResult, len(taskModel.Hosts))
|
||||||
|
for _, taskHost := range taskModel.Hosts {
|
||||||
|
go func(th models.TaskHostDetail) {
|
||||||
|
output, err := rpcClient.Exec(th.Name, th.Port, taskRequest)
|
||||||
|
errorMessage := ""
|
||||||
|
if err != nil {
|
||||||
|
errorMessage = err.Error()
|
||||||
|
}
|
||||||
|
outputMessage := fmt.Sprintf("主机: [%s-%s:%d]\n%s\n%s\n\n",
|
||||||
|
th.Alias, th.Name, th.Port, errorMessage, output,
|
||||||
|
)
|
||||||
|
resultChan <- TaskResult{Err: err, Result: outputMessage}
|
||||||
|
}(taskHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
var aggregationErr error = nil
|
||||||
|
aggregationResult := ""
|
||||||
|
for i := 0; i < len(taskModel.Hosts); i++ {
|
||||||
|
taskResult := <-resultChan
|
||||||
|
aggregationResult += taskResult.Result
|
||||||
|
if taskResult.Err != nil {
|
||||||
|
aggregationErr = taskResult.Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aggregationResult, aggregationErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建任务日志
|
||||||
|
func createTaskLog(taskModel models.Task, status models.Status) (int64, error) {
|
||||||
|
taskLogModel := new(models.TaskLog)
|
||||||
|
taskLogModel.TaskId = taskModel.Id
|
||||||
|
taskLogModel.Name = taskModel.Name
|
||||||
|
taskLogModel.Spec = taskModel.Spec
|
||||||
|
taskLogModel.Protocol = taskModel.Protocol
|
||||||
|
taskLogModel.Command = taskModel.Command
|
||||||
|
taskLogModel.Timeout = taskModel.Timeout
|
||||||
|
if taskModel.Protocol == models.TaskRPC {
|
||||||
|
aggregationHost := ""
|
||||||
|
for _, host := range taskModel.Hosts {
|
||||||
|
aggregationHost += fmt.Sprintf("%s - %s<br>", host.Alias, host.Name)
|
||||||
|
}
|
||||||
|
taskLogModel.Hostname = aggregationHost
|
||||||
|
}
|
||||||
|
taskLogModel.StartTime = time.Now()
|
||||||
|
taskLogModel.Status = status
|
||||||
|
insertId, err := taskLogModel.Create()
|
||||||
|
|
||||||
|
return insertId, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新任务日志
|
||||||
|
func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) {
|
||||||
|
taskLogModel := new(models.TaskLog)
|
||||||
|
var status models.Status
|
||||||
|
result := taskResult.Result
|
||||||
|
if taskResult.Err != nil {
|
||||||
|
status = models.Failure
|
||||||
|
} else {
|
||||||
|
status = models.Finish
|
||||||
|
}
|
||||||
|
return taskLogModel.Update(taskLogId, models.CommonMap{
|
||||||
|
"retry_times": taskResult.RetryTimes,
|
||||||
|
"status": status,
|
||||||
|
"result": result,
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func createJob(taskModel models.Task) cron.FuncJob {
|
||||||
|
handler := createHandler(taskModel)
|
||||||
|
if handler == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
taskFunc := func() {
|
||||||
|
taskCount.Add()
|
||||||
|
defer taskCount.Done()
|
||||||
|
|
||||||
|
taskLogId := beforeExecJob(taskModel)
|
||||||
|
if taskLogId <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if taskModel.Multi == 0 {
|
||||||
|
runInstance.add(taskModel.Id)
|
||||||
|
defer runInstance.done(taskModel.Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
concurrencyQueue.Add()
|
||||||
|
defer concurrencyQueue.Done()
|
||||||
|
|
||||||
|
logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||||
|
taskResult := execJob(handler, taskModel, taskLogId)
|
||||||
|
logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command)
|
||||||
|
afterExecJob(taskModel, taskResult, taskLogId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return taskFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func createHandler(taskModel models.Task) Handler {
|
||||||
|
var handler Handler = nil
|
||||||
|
switch taskModel.Protocol {
|
||||||
|
case models.TaskHTTP:
|
||||||
|
handler = new(HTTPHandler)
|
||||||
|
case models.TaskRPC:
|
||||||
|
handler = new(RPCHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务前置操作
|
||||||
|
func beforeExecJob(taskModel models.Task) (taskLogId int64) {
|
||||||
|
if taskModel.Multi == 0 && runInstance.has(taskModel.Id) {
|
||||||
|
createTaskLog(taskModel, models.Cancel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
taskLogId, err := createTaskLog(taskModel, models.Running)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("任务开始执行#写入任务日志失败-", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Debugf("任务命令-%s", taskModel.Command)
|
||||||
|
|
||||||
|
return taskLogId
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务执行后置操作
|
||||||
|
func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) {
|
||||||
|
_, err := updateTaskLog(taskLogId, taskResult)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("任务结束#更新任务日志失败-", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送邮件
|
||||||
|
go SendNotification(taskModel, taskResult)
|
||||||
|
// 执行依赖任务
|
||||||
|
go execDependencyTask(taskModel, taskResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行依赖任务, 多个任务并发执行
|
||||||
|
func execDependencyTask(taskModel models.Task, taskResult TaskResult) {
|
||||||
|
// 父任务才能执行子任务
|
||||||
|
if taskModel.Level != models.TaskLevelParent {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否存在子任务
|
||||||
|
dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId)
|
||||||
|
if dependencyTaskId == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务
|
||||||
|
if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil {
|
||||||
|
logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取子任务
|
||||||
|
model := new(models.Task)
|
||||||
|
tasks, err := model.GetDependencyTaskList(dependencyTaskId)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(tasks) == 0 {
|
||||||
|
logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id)
|
||||||
|
}
|
||||||
|
for _, task := range tasks {
|
||||||
|
task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id)
|
||||||
|
ServiceTask.Run(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送任务结果通知
|
||||||
|
func SendNotification(taskModel models.Task, taskResult TaskResult) {
|
||||||
|
var statusName string
|
||||||
|
// 未开启通知
|
||||||
|
if taskModel.NotifyStatus == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if taskModel.NotifyStatus == 3 {
|
||||||
|
// 关键字匹配通知
|
||||||
|
if !strings.Contains(taskResult.Result, taskModel.NotifyKeyword) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if taskModel.NotifyStatus == 1 && taskResult.Err == nil {
|
||||||
|
// 执行失败才发送通知
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if taskModel.NotifyType != 3 && taskModel.NotifyReceiverId == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if taskResult.Err != nil {
|
||||||
|
statusName = "失败"
|
||||||
|
} else {
|
||||||
|
statusName = "成功"
|
||||||
|
}
|
||||||
|
// 发送通知
|
||||||
|
msg := notify.Message{
|
||||||
|
"task_type": taskModel.NotifyType,
|
||||||
|
"task_receiver_id": taskModel.NotifyReceiverId,
|
||||||
|
"name": taskModel.Name,
|
||||||
|
"output": taskResult.Result,
|
||||||
|
"status": statusName,
|
||||||
|
"task_id": taskModel.Id,
|
||||||
|
}
|
||||||
|
notify.Push(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行具体任务
|
||||||
|
func execJob(handler Handler, taskModel models.Task, taskUniqueId int64) TaskResult {
|
||||||
|
defer func() {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
logger.Error("panic#service/task.go:execJob#", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// 默认只运行任务一次
|
||||||
|
var execTimes int8 = 1
|
||||||
|
if taskModel.RetryTimes > 0 {
|
||||||
|
execTimes += taskModel.RetryTimes
|
||||||
|
}
|
||||||
|
var i int8 = 0
|
||||||
|
var output string
|
||||||
|
var err error
|
||||||
|
for i < execTimes {
|
||||||
|
output, err = handler.Run(taskModel, taskUniqueId)
|
||||||
|
if err == nil {
|
||||||
|
return TaskResult{Result: output, Err: err, RetryTimes: i}
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
if i < execTimes {
|
||||||
|
logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error())
|
||||||
|
if taskModel.RetryInterval > 0 {
|
||||||
|
time.Sleep(time.Duration(taskModel.RetryInterval) * time.Second)
|
||||||
|
} else {
|
||||||
|
// 默认重试间隔时间,每次递增1分钟
|
||||||
|
time.Sleep(time.Duration(i) * time.Minute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
.PHONY: build
|
||||||
|
build: gocron node
|
||||||
|
|
||||||
|
.PHONY: build-race
|
||||||
|
build-race: enable-race build
|
||||||
|
|
||||||
|
.PHONY: run
|
||||||
|
run: build kill
|
||||||
|
./bin/gocron-node &
|
||||||
|
./bin/gocron web -e dev
|
||||||
|
|
||||||
|
.PHONY: run-race
|
||||||
|
run-race: enable-race run
|
||||||
|
|
||||||
|
.PHONY: kill
|
||||||
|
kill:
|
||||||
|
-killall gocron-node
|
||||||
|
|
||||||
|
.PHONY: gocron
|
||||||
|
gocron:
|
||||||
|
go build $(RACE) -o bin/gocron ./cmd/gocron
|
||||||
|
|
||||||
|
.PHONY: node
|
||||||
|
node:
|
||||||
|
go build $(RACE) -o bin/gocron-node ./cmd/node
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test $(RACE) ./...
|
||||||
|
|
||||||
|
.PHONY: test-race
|
||||||
|
test-race: enable-race test
|
||||||
|
|
||||||
|
.PHONY: enable-race
|
||||||
|
enable-race:
|
||||||
|
$(eval RACE = -race)
|
||||||
|
|
||||||
|
.PHONY: package
|
||||||
|
package: build-vue statik
|
||||||
|
bash ./package.sh
|
||||||
|
|
||||||
|
.PHONY: package-all
|
||||||
|
package-all: build-vue statik
|
||||||
|
bash ./package.sh -p 'linux darwin windows'
|
||||||
|
|
||||||
|
.PHONY: build-vue
|
||||||
|
build-vue:
|
||||||
|
cd web/vue && yarn run build
|
||||||
|
cp -r web/vue/dist/ web/public/
|
||||||
|
|
||||||
|
.PHONY: install-vue
|
||||||
|
install-vue:
|
||||||
|
cd web/vue && yarn install
|
||||||
|
|
||||||
|
.PHONY: statik
|
||||||
|
statik:
|
||||||
|
go get github.com/rakyll/statik
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
rm bin/gocron
|
||||||
|
rm bin/gocron-node
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 主机
|
|
||||||
type Host struct {
|
|
||||||
Id int16 `xorm:"smallint pk autoincr"`
|
|
||||||
Name string `xorm:"varchar(64) notnull"` // 主机名称
|
|
||||||
Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名
|
|
||||||
Port int `xorm:"notnull default 22"` // 主机端口
|
|
||||||
Remark string `xorm:"varchar(100) notnull default '' "` // 备注
|
|
||||||
BaseModel `xorm:"-"`
|
|
||||||
Selected bool `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增
|
|
||||||
func (host *Host) Create() (insertId int16, err error) {
|
|
||||||
_, err = Db.Insert(host)
|
|
||||||
if err == nil {
|
|
||||||
insertId = host.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) UpdateBean(id int16) (int64, error) {
|
|
||||||
return Db.ID(id).Cols("name,alias,port,remark").Update(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
func (host *Host) Update(id int, data CommonMap) (int64, error) {
|
|
||||||
return Db.Table(host).ID(id).Update(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
func (host *Host) Delete(id int) (int64, error) {
|
|
||||||
return Db.Id(id).Delete(new(Host))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) Find(id int) error {
|
|
||||||
_, err := Db.Id(id).Get(host)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) NameExists(name string, id int16) (bool, error) {
|
|
||||||
if id == 0 {
|
|
||||||
count, err := Db.Where("name = ?", name).Count(host);
|
|
||||||
return count > 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := Db.Where("name = ? AND id != ?", name, id).Count(host);
|
|
||||||
return count > 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) List(params CommonMap) ([]Host, error) {
|
|
||||||
host.parsePageAndPageSize(params)
|
|
||||||
list := make([]Host, 0)
|
|
||||||
session := Db.Desc("id")
|
|
||||||
host.parseWhere(session, params)
|
|
||||||
err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list)
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) AllList() ([]Host, error) {
|
|
||||||
list := make([]Host, 0)
|
|
||||||
err := Db.Cols("name,port").Desc("id").Find(&list)
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (host *Host) Total(params CommonMap) (int64, error) {
|
|
||||||
session := Db.NewSession()
|
|
||||||
host.parseWhere(session, params)
|
|
||||||
return session.Count(host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析where
|
|
||||||
func (host *Host) parseWhere(session *xorm.Session, params CommonMap) {
|
|
||||||
if len(params) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
id, ok := params["Id"]
|
|
||||||
if ok && id.(int) > 0 {
|
|
||||||
session.And("id = ?", id)
|
|
||||||
}
|
|
||||||
name, ok := params["Name"]
|
|
||||||
if ok && name.(string) != "" {
|
|
||||||
session.And("name = ?", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 用户登录日志
|
|
||||||
|
|
||||||
type LoginLog struct {
|
|
||||||
Id int `xorm:"pk autoincr notnull "`
|
|
||||||
Username string `xorm:"varchar(32) notnull"`
|
|
||||||
Ip string `xorm:"varchar(15) not null"`
|
|
||||||
Created time.Time `xorm:"datetime notnull created"`
|
|
||||||
BaseModel `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (log *LoginLog) Create() (insertId int, err error) {
|
|
||||||
_, err = Db.Insert(log)
|
|
||||||
if err == nil {
|
|
||||||
insertId = log.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) {
|
|
||||||
log.parsePageAndPageSize(params)
|
|
||||||
list := make([]LoginLog, 0)
|
|
||||||
err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list)
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (log *LoginLog) Total() (int64, error) {
|
|
||||||
return Db.Count(log)
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 创建数据库表
|
|
||||||
|
|
||||||
type Migration struct{}
|
|
||||||
|
|
||||||
func (migration *Migration) Exec(dbName string) error {
|
|
||||||
if !isDatabaseExist(dbName) {
|
|
||||||
return errors.New("数据库不存在")
|
|
||||||
}
|
|
||||||
setting := new(Setting)
|
|
||||||
task := new(Task)
|
|
||||||
tables := []interface{}{
|
|
||||||
&User{}, task, &TaskLog{}, &Host{}, setting,&LoginLog{},&TaskHost{},
|
|
||||||
}
|
|
||||||
for _, table := range tables {
|
|
||||||
exist, err:= Db.IsTableExist(table)
|
|
||||||
if exist {
|
|
||||||
return errors.New("数据表已存在")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = Db.Sync2(table)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setting.InitBasicField()
|
|
||||||
task.CreateTestTask()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建数据库
|
|
||||||
func isDatabaseExist(name string) bool {
|
|
||||||
_, err := Db.Exec("use ?", name)
|
|
||||||
|
|
||||||
return err != nil
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"github.com/go-xorm/core"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
"gopkg.in/macaron.v1"
|
|
||||||
"strings"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"github.com/ouqiang/gocron/modules/app"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Status int8
|
|
||||||
type CommonMap map[string]interface{}
|
|
||||||
|
|
||||||
var TablePrefix string = ""
|
|
||||||
var Db *xorm.Engine
|
|
||||||
|
|
||||||
const (
|
|
||||||
Disabled Status = 0 // 禁用
|
|
||||||
Failure Status = 0 // 失败
|
|
||||||
Enabled Status = 1 // 启用
|
|
||||||
Running Status = 1 // 运行中
|
|
||||||
Finish Status = 2 // 完成
|
|
||||||
Cancel Status = 3 // 取消
|
|
||||||
Waiting Status = 5 // 等待中
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
Page = 1 // 当前页数
|
|
||||||
PageSize = 20 // 每页多少条数据
|
|
||||||
MaxPageSize = 100000 // 每次最多取多少条
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultTimeFormat = "2006-01-02 15:04:05"
|
|
||||||
|
|
||||||
type BaseModel struct {
|
|
||||||
Page int `xorm:"-"`
|
|
||||||
PageSize int `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (model *BaseModel) parsePageAndPageSize(params CommonMap) {
|
|
||||||
page, ok := params["Page"]
|
|
||||||
if ok {
|
|
||||||
model.Page = page.(int)
|
|
||||||
}
|
|
||||||
pageSize, ok := params["PageSize"]
|
|
||||||
if ok {
|
|
||||||
model.PageSize = pageSize.(int)
|
|
||||||
}
|
|
||||||
if model.Page <= 0 {
|
|
||||||
model.Page = Page
|
|
||||||
}
|
|
||||||
if model.PageSize <= 0 {
|
|
||||||
model.PageSize = MaxPageSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (model *BaseModel) pageLimitOffset() int {
|
|
||||||
return (model.Page - 1) * model.PageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建Db
|
|
||||||
func CreateDb() *xorm.Engine {
|
|
||||||
config := getDbConfig()
|
|
||||||
dsn := getDbEngineDSN(config["engine"], config)
|
|
||||||
engine, err := xorm.NewEngine(config["engine"], dsn)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal("创建xorm引擎失败", err)
|
|
||||||
}
|
|
||||||
maxIdleConns, err := strconv.Atoi(config["max_idle_conns"])
|
|
||||||
maxOpenConns, err := strconv.Atoi(config["max_open_conns"])
|
|
||||||
if maxIdleConns <= 0 {
|
|
||||||
maxIdleConns = 30
|
|
||||||
}
|
|
||||||
if maxOpenConns <= 0 {
|
|
||||||
maxOpenConns = 100
|
|
||||||
}
|
|
||||||
engine.SetMaxIdleConns(maxIdleConns)
|
|
||||||
engine.SetMaxOpenConns(maxOpenConns)
|
|
||||||
|
|
||||||
if config["prefix"] != "" {
|
|
||||||
// 设置表前缀
|
|
||||||
TablePrefix = config["prefix"]
|
|
||||||
mapper := core.NewPrefixMapper(core.SnakeMapper{}, config["prefix"])
|
|
||||||
engine.SetTableMapper(mapper)
|
|
||||||
}
|
|
||||||
// 本地环境开启日志
|
|
||||||
if macaron.Env == macaron.DEV {
|
|
||||||
engine.ShowSQL(true)
|
|
||||||
engine.Logger().SetLevel(core.LOG_DEBUG)
|
|
||||||
}
|
|
||||||
|
|
||||||
go keepDbAlived(engine)
|
|
||||||
|
|
||||||
return engine
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建临时数据库连接
|
|
||||||
func CreateTmpDb(config map[string]string) (*xorm.Engine, error) {
|
|
||||||
dsn := getDbEngineDSN(config["engine"], config)
|
|
||||||
|
|
||||||
return xorm.NewEngine(config["engine"], dsn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取数据库引擎DSN mysql,sqlite
|
|
||||||
func getDbEngineDSN(engine string, config map[string]string) string {
|
|
||||||
engine = strings.ToLower(engine)
|
|
||||||
var dsn string = ""
|
|
||||||
switch engine {
|
|
||||||
case "mysql":
|
|
||||||
dsn = fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s",
|
|
||||||
config["user"],
|
|
||||||
config["password"],
|
|
||||||
config["host"],
|
|
||||||
config["port"],
|
|
||||||
config["database"],
|
|
||||||
config["charset"])
|
|
||||||
}
|
|
||||||
|
|
||||||
return dsn
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 获取数据库配置
|
|
||||||
func getDbConfig() map[string]string {
|
|
||||||
var db map[string]string = make(map[string]string)
|
|
||||||
db["user"] = app.Setting.Key("db.user").String()
|
|
||||||
db["password"] = app.Setting.Key("db.password").String()
|
|
||||||
db["host"] = app.Setting.Key("db.host").String()
|
|
||||||
db["port"] = app.Setting.Key("db.port").String()
|
|
||||||
db["database"] = app.Setting.Key("db.database").String()
|
|
||||||
db["charset"] = app.Setting.Key("db.charset").String()
|
|
||||||
db["prefix"] = app.Setting.Key("db.prefix").String()
|
|
||||||
db["engine"] = app.Setting.Key("db.engine").String()
|
|
||||||
db["max_idle_conns"] = app.Setting.Key("db.max.idle.conns").String()
|
|
||||||
db["max_open_conns"] = app.Setting.Key("db.max.open.conns").String()
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
func keepDbAlived(engine *xorm.Engine) {
|
|
||||||
t := time.Tick(180 * time.Second)
|
|
||||||
for {
|
|
||||||
<- t
|
|
||||||
engine.Ping()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Setting struct {
|
|
||||||
Id int `xorm:"int pk autoincr"`
|
|
||||||
Code string `xorm:"varchar(32) notnull"`
|
|
||||||
Key string `xorm:"varchar(64) notnull"`
|
|
||||||
Value string `xorm:"varchar(4096) notnull default '' "`
|
|
||||||
}
|
|
||||||
|
|
||||||
const SlackCode = "slack"
|
|
||||||
const SlackUrlKey = "url"
|
|
||||||
const SlackChannelKey = "channel"
|
|
||||||
|
|
||||||
const MailCode = "mail"
|
|
||||||
const MailServerKey = "server"
|
|
||||||
const MailUserKey = "user"
|
|
||||||
|
|
||||||
// 初始化基本字段 邮件、slack等
|
|
||||||
func (setting *Setting) InitBasicField() {
|
|
||||||
setting.Code = SlackCode;
|
|
||||||
setting.Key = SlackUrlKey
|
|
||||||
Db.Insert(setting)
|
|
||||||
|
|
||||||
setting.Id = 0
|
|
||||||
setting.Code = MailCode
|
|
||||||
setting.Key = MailServerKey
|
|
||||||
Db.Insert(setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
// region slack配置
|
|
||||||
|
|
||||||
type Slack struct {
|
|
||||||
Url string
|
|
||||||
Channels []Channel
|
|
||||||
}
|
|
||||||
|
|
||||||
type Channel struct {
|
|
||||||
Id int
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) Slack() (Slack, error) {
|
|
||||||
list := make([]Setting, 0)
|
|
||||||
err := Db.Where("code = ?", SlackCode).Find(&list)
|
|
||||||
slack := Slack{Url:"", Channels:make([]Channel, 0)}
|
|
||||||
if err != nil {
|
|
||||||
return slack, err
|
|
||||||
}
|
|
||||||
|
|
||||||
setting.formatSlack(list, &slack)
|
|
||||||
|
|
||||||
return slack, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) formatSlack(list []Setting, slack *Slack) {
|
|
||||||
for _, v := range list {
|
|
||||||
if v.Key == SlackUrlKey {
|
|
||||||
slack.Url = v.Value
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
slack.Channels = append(slack.Channels, Channel{
|
|
||||||
v.Id, v.Value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新slack webhook url
|
|
||||||
func (setting *Setting) UpdateSlackUrl(url string) (int64, error) {
|
|
||||||
setting.Value = url
|
|
||||||
|
|
||||||
return Db.Cols("value").Update(setting, Setting{Code:SlackCode, Key:SlackUrlKey})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 创建slack渠道
|
|
||||||
func (setting *Setting) CreateChannel(channel string) (int64, error) {
|
|
||||||
setting.Code = SlackCode
|
|
||||||
setting.Key = SlackChannelKey
|
|
||||||
setting.Value = channel
|
|
||||||
|
|
||||||
return Db.Insert(setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) IsChannelExist(channel string) (bool) {
|
|
||||||
setting.Code = SlackCode
|
|
||||||
setting.Key = SlackChannelKey
|
|
||||||
setting.Value = channel
|
|
||||||
|
|
||||||
count, _ := Db.Count(setting)
|
|
||||||
|
|
||||||
return count > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除slack渠道
|
|
||||||
func (setting *Setting) RemoveChannel(id int) (int64, error) {
|
|
||||||
setting.Code = SlackCode
|
|
||||||
setting.Key = SlackChannelKey
|
|
||||||
setting.Id = id
|
|
||||||
return Db.Delete(setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
type Mail struct {
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
MailUsers []MailUser
|
|
||||||
}
|
|
||||||
|
|
||||||
type MailUser struct {
|
|
||||||
Id int
|
|
||||||
Username string
|
|
||||||
Email string
|
|
||||||
}
|
|
||||||
|
|
||||||
// region 邮件配置
|
|
||||||
func (setting *Setting) Mail() (Mail, error) {
|
|
||||||
list := make([]Setting, 0)
|
|
||||||
err := Db.Where("code = ?", MailCode).Find(&list)
|
|
||||||
mail := Mail{MailUsers:make([]MailUser, 0)}
|
|
||||||
if err != nil {
|
|
||||||
return mail, err
|
|
||||||
}
|
|
||||||
|
|
||||||
setting.formatMail(list, &mail)
|
|
||||||
|
|
||||||
return mail, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) formatMail(list []Setting, mail *Mail) {
|
|
||||||
mailUser := MailUser{}
|
|
||||||
for _, v := range list {
|
|
||||||
if v.Key == MailServerKey {
|
|
||||||
json.Unmarshal([]byte(v.Value), mail)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
json.Unmarshal([]byte(v.Value), &mailUser)
|
|
||||||
mailUser.Id = v.Id
|
|
||||||
mail.MailUsers = append(mail.MailUsers, mailUser)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) UpdateMailServer(config string) (int64, error) {
|
|
||||||
setting.Value = config
|
|
||||||
return Db.Cols("value").Update(setting, Setting{Code:MailCode, Key:MailServerKey})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) CreateMailUser(username, email string) (int64, error) {
|
|
||||||
setting.Code = MailCode
|
|
||||||
setting.Key = MailUserKey
|
|
||||||
mailUser := MailUser{0, username, email}
|
|
||||||
jsonByte, err := json.Marshal(mailUser)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
setting.Value = string(jsonByte)
|
|
||||||
|
|
||||||
return Db.Insert(setting)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (setting *Setting) RemoveMailUser(id int) (int64, error) {
|
|
||||||
setting.Code = MailCode
|
|
||||||
setting.Key = MailUserKey
|
|
||||||
setting.Id = id
|
|
||||||
return Db.Delete(setting)
|
|
||||||
}
|
|
||||||
// endregion
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskProtocol int8
|
|
||||||
|
|
||||||
const (
|
|
||||||
TaskHTTP TaskProtocol = iota + 1 // HTTP协议
|
|
||||||
TaskRPC // RPC方式执行命令
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskLevel int8
|
|
||||||
|
|
||||||
const (
|
|
||||||
TaskLevelParent TaskLevel = 1 // 父任务
|
|
||||||
TaskLevelChild TaskLevel = 2 // 子任务(依赖任务)
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskDependencyStatus int8
|
|
||||||
|
|
||||||
const (
|
|
||||||
TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖
|
|
||||||
TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖
|
|
||||||
)
|
|
||||||
|
|
||||||
// 任务
|
|
||||||
type Task struct {
|
|
||||||
Id int `xorm:"int pk autoincr"`
|
|
||||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
|
||||||
Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务
|
|
||||||
DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔
|
|
||||||
DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖
|
|
||||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
|
||||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令
|
|
||||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
|
||||||
Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行
|
|
||||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数
|
|
||||||
NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知
|
|
||||||
NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack
|
|
||||||
NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔
|
|
||||||
Remark string `xorm:"varchar(100) notnull default ''"` // 备注
|
|
||||||
Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止
|
|
||||||
Created time.Time `xorm:"datetime notnull created"` // 创建时间
|
|
||||||
Deleted time.Time `xorm:"datetime deleted"` // 删除时间
|
|
||||||
BaseModel `xorm:"-"`
|
|
||||||
Hosts []TaskHostDetail `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func taskHostTableName() []string {
|
|
||||||
return []string{TablePrefix + "task_host", "th"}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增
|
|
||||||
func (task *Task) Create() (insertId int, err error) {
|
|
||||||
_, err = Db.Insert(task)
|
|
||||||
if err == nil {
|
|
||||||
insertId = task.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增测试任务
|
|
||||||
func (task *Task) CreateTestTask() {
|
|
||||||
// HTTP任务
|
|
||||||
task.Name = "测试HTTP任务"
|
|
||||||
task.Level = TaskLevelParent
|
|
||||||
task.Protocol = TaskHTTP
|
|
||||||
task.Spec = "*/30 * * * * *"
|
|
||||||
// 查询IP地址区域信息
|
|
||||||
task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253"
|
|
||||||
task.Status = Enabled
|
|
||||||
task.Create()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (task *Task) UpdateBean(id int) (int64, error) {
|
|
||||||
return Db.ID(id).
|
|
||||||
Cols("name,spec,protocol,command,timeout,multi,retry_times,remark,notify_status,notify_type,notify_receiver_id, dependency_task_id, dependency_status").
|
|
||||||
Update(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
func (task *Task) Update(id int, data CommonMap) (int64, error) {
|
|
||||||
return Db.Table(task).ID(id).Update(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
func (task *Task) Delete(id int) (int64, error) {
|
|
||||||
return Db.Id(id).Delete(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁用
|
|
||||||
func (task *Task) Disable(id int) (int64, error) {
|
|
||||||
return task.Update(id, CommonMap{"status": Disabled})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 激活
|
|
||||||
func (task *Task) Enable(id int) (int64, error) {
|
|
||||||
return task.Update(id, CommonMap{"status": Enabled})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有激活任务
|
|
||||||
func (task *Task) ActiveList() ([]Task, error) {
|
|
||||||
list := make([]Task, 0)
|
|
||||||
err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
|
||||||
Find(&list)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task.setHostsForTasks(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取某个主机下的所有激活任务
|
|
||||||
func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) {
|
|
||||||
taskHostModel := new(TaskHost)
|
|
||||||
taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
list := make([]Task, 0)
|
|
||||||
err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent).
|
|
||||||
In("id", taskIds...).
|
|
||||||
Find(&list)
|
|
||||||
if err != nil {
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task.setHostsForTasks(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) {
|
|
||||||
taskHostModel := new(TaskHost)
|
|
||||||
var err error
|
|
||||||
for i, value := range tasks {
|
|
||||||
taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tasks[i].Hosts = taskHostDetails
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断任务名称是否存在
|
|
||||||
func (task *Task) NameExist(name string, id int) (bool, error) {
|
|
||||||
if id > 0 {
|
|
||||||
count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task);
|
|
||||||
return count > 0, err
|
|
||||||
}
|
|
||||||
count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task);
|
|
||||||
|
|
||||||
return count > 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (task *Task) GetStatus(id int) (Status, error) {
|
|
||||||
exist, err := Db.Id(id).Get(task)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
if !exist {
|
|
||||||
return 0, errors.New("not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
return task.Status, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(task *Task) Detail(id int) (Task, error) {
|
|
||||||
t := Task{}
|
|
||||||
_, err := Db.Where("id=?", id).Get(&t)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
|
|
||||||
taskHostModel := new(TaskHost)
|
|
||||||
t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id)
|
|
||||||
|
|
||||||
return t, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (task *Task) List(params CommonMap) ([]Task, error) {
|
|
||||||
task.parsePageAndPageSize(params)
|
|
||||||
list := make([]Task, 0)
|
|
||||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
|
||||||
task.parseWhere(session, params)
|
|
||||||
err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task.setHostsForTasks(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取依赖任务列表
|
|
||||||
func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) {
|
|
||||||
list := make([]Task, 0)
|
|
||||||
if ids == "" {
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
idList := strings.Split(ids, ",")
|
|
||||||
taskIds := make([]interface{}, len(idList))
|
|
||||||
for i, v := range idList {
|
|
||||||
taskIds[i] = v
|
|
||||||
}
|
|
||||||
fields := "t.*"
|
|
||||||
err := Db.Alias("t").
|
|
||||||
Where("t.level = ?", TaskLevelChild).
|
|
||||||
In("t.id", taskIds).
|
|
||||||
Cols(fields).
|
|
||||||
Find(&list)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return task.setHostsForTasks(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (task *Task) Total(params CommonMap) (int64, error) {
|
|
||||||
session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id")
|
|
||||||
task.parseWhere(session, params)
|
|
||||||
return session.GroupBy("t.id").Count(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析where
|
|
||||||
func (task *Task) parseWhere(session *xorm.Session, params CommonMap) {
|
|
||||||
if len(params) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
id, ok := params["Id"]
|
|
||||||
if ok && id.(int) > 0 {
|
|
||||||
session.And("t.id = ?", id)
|
|
||||||
}
|
|
||||||
hostId, ok := params["HostId"]
|
|
||||||
if ok && hostId.(int) > 0 {
|
|
||||||
session.And("th.host_id = ?", hostId)
|
|
||||||
}
|
|
||||||
name, ok := params["Name"]
|
|
||||||
if ok && name.(string) != "" {
|
|
||||||
session.And("t.name LIKE ?", "%" + name.(string) + "%")
|
|
||||||
}
|
|
||||||
protocol, ok := params["Protocol"]
|
|
||||||
if ok && protocol.(int) > 0 {
|
|
||||||
session.And("protocol = ?", protocol)
|
|
||||||
}
|
|
||||||
status, ok := params["Status"]
|
|
||||||
if ok && status.(int) > -1 {
|
|
||||||
session.And("status = ?", status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
|
|
||||||
type TaskHost struct {
|
|
||||||
Id int `xorm:"int pk autoincr"`
|
|
||||||
TaskId int `xorm:"int not null index"`
|
|
||||||
HostId int16 `xorm:"smallint not null index"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type TaskHostDetail struct {
|
|
||||||
TaskHost `xorm:"extends"`
|
|
||||||
Name string
|
|
||||||
Port int
|
|
||||||
Alias string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (TaskHostDetail) TableName() string {
|
|
||||||
return TablePrefix + "task_host"
|
|
||||||
}
|
|
||||||
|
|
||||||
func hostTableName() []string {
|
|
||||||
return []string{TablePrefix + "host", "h"}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *TaskHost) Remove(taskId int) error {
|
|
||||||
_, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost))
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *TaskHost) Add(taskId int, hostIds []int) error {
|
|
||||||
|
|
||||||
err := th.Remove(taskId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
taskHosts := make([]TaskHost, len(hostIds))
|
|
||||||
for i, value := range hostIds {
|
|
||||||
taskHosts[i].TaskId = taskId
|
|
||||||
taskHosts[i].HostId = int16(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = Db.Insert(&taskHosts)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) {
|
|
||||||
list := make([]TaskHostDetail, 0)
|
|
||||||
fields := "th.id,th.host_id,h.alias,h.name,h.port"
|
|
||||||
err := Db.Alias("th").
|
|
||||||
Join("LEFT", hostTableName(), "th.host_id=h.id").
|
|
||||||
Where("th.task_id = ?", taskId).
|
|
||||||
Cols(fields).
|
|
||||||
Find(&list)
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) {
|
|
||||||
list := make([]TaskHost, 0)
|
|
||||||
err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
taskIds := make([]interface{}, len(list))
|
|
||||||
for i, value := range list {
|
|
||||||
taskIds[i] = value.TaskId
|
|
||||||
}
|
|
||||||
|
|
||||||
return taskIds, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断主机id是否有引用
|
|
||||||
func (th *TaskHost) HostIdExist(hostId int16) (bool, error) {
|
|
||||||
count, err := Db.Where("host_id = ?", hostId).Count(th);
|
|
||||||
|
|
||||||
return count > 0, err
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"github.com/go-xorm/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TaskType int8
|
|
||||||
|
|
||||||
|
|
||||||
// 任务执行日志
|
|
||||||
type TaskLog struct {
|
|
||||||
Id int64 `xorm:"bigint pk autoincr"`
|
|
||||||
TaskId int `xorm:"int notnull index default 0"` // 任务id
|
|
||||||
Name string `xorm:"varchar(32) notnull"` // 任务名称
|
|
||||||
Spec string `xorm:"varchar(64) notnull"` // crontab
|
|
||||||
Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC
|
|
||||||
Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令
|
|
||||||
Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制
|
|
||||||
RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数
|
|
||||||
Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔
|
|
||||||
StartTime time.Time `xorm:"datetime created"` // 开始执行时间
|
|
||||||
EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间
|
|
||||||
Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
|
||||||
Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果
|
|
||||||
TotalTime int `xorm:"-"` // 执行总时长
|
|
||||||
BaseModel `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (taskLog *TaskLog) Create() (insertId int64, err error) {
|
|
||||||
_, err = Db.Insert(taskLog)
|
|
||||||
if err == nil {
|
|
||||||
insertId = taskLog.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) {
|
|
||||||
return Db.Table(taskLog).ID(id).Update(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) {
|
|
||||||
taskLog.parsePageAndPageSize(params)
|
|
||||||
list := make([]TaskLog, 0)
|
|
||||||
session := Db.Desc("id")
|
|
||||||
taskLog.parseWhere(session, params)
|
|
||||||
err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list)
|
|
||||||
if len(list) > 0 {
|
|
||||||
for i, item := range list {
|
|
||||||
endTime := item.EndTime
|
|
||||||
if item.Status == Running {
|
|
||||||
endTime = time.Now()
|
|
||||||
}
|
|
||||||
execSeconds := endTime.Sub(item.StartTime).Seconds()
|
|
||||||
list[i].TotalTime = int(execSeconds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空表
|
|
||||||
func (taskLog *TaskLog) Clear() (int64, error) {
|
|
||||||
return Db.Where("1=1").Delete(taskLog);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除N个月前的日志
|
|
||||||
func (taskLog *TaskLog) Remove(id int) (int64, error) {
|
|
||||||
t := time.Now().AddDate(0, -id, 0)
|
|
||||||
return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (taskLog *TaskLog) Total(params CommonMap) (int64, error) {
|
|
||||||
session := Db.NewSession()
|
|
||||||
defer session.Close()
|
|
||||||
taskLog.parseWhere(session, params)
|
|
||||||
return session.Count(taskLog)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析where
|
|
||||||
func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) {
|
|
||||||
if len(params) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
taskId, ok := params["TaskId"]
|
|
||||||
if ok && taskId.(int) > 0 {
|
|
||||||
session.And("task_id = ?", taskId)
|
|
||||||
}
|
|
||||||
protocol, ok := params["Protocol"]
|
|
||||||
if ok && protocol.(int) > 0 {
|
|
||||||
session.And("protocol = ?", protocol)
|
|
||||||
}
|
|
||||||
status, ok := params["Status"]
|
|
||||||
if ok && status.(int) > -1 {
|
|
||||||
session.And("status = ?", status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const PasswordSaltLength = 6
|
|
||||||
|
|
||||||
// 用户model
|
|
||||||
type User struct {
|
|
||||||
Id int `xorm:"pk autoincr notnull "`
|
|
||||||
Name string `xorm:"varchar(32) notnull unique"` // 用户名
|
|
||||||
Password string `xorm:"char(32) notnull "` // 密码
|
|
||||||
Salt string `xorm:"char(6) notnull "` // 密码盐值
|
|
||||||
Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱
|
|
||||||
Created time.Time `xorm:"datetime notnull created"`
|
|
||||||
Updated time.Time `xorm:"datetime updated"`
|
|
||||||
Deleted time.Time `xorm:"datetime deleted"`
|
|
||||||
IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户
|
|
||||||
Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用
|
|
||||||
BaseModel `xorm:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增
|
|
||||||
func (user *User) Create() (insertId int, err error) {
|
|
||||||
user.Status = Enabled
|
|
||||||
user.Salt = user.generateSalt()
|
|
||||||
user.Password = user.encryptPassword(user.Password, user.Salt)
|
|
||||||
|
|
||||||
_, err = Db.Insert(user)
|
|
||||||
if err == nil {
|
|
||||||
insertId = user.Id
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
func (user *User) Update(id int, data CommonMap) (int64, error) {
|
|
||||||
return Db.Table(user).ID(id).Update(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) UpdatePassword(id int, password string) (int64, error) {
|
|
||||||
salt := user.generateSalt()
|
|
||||||
safePassword := user.encryptPassword(password, salt)
|
|
||||||
|
|
||||||
return user.Update(id, CommonMap{"password": safePassword, "salt": salt})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除
|
|
||||||
func (user *User) Delete(id int) (int64, error) {
|
|
||||||
return Db.Id(id).Delete(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 禁用
|
|
||||||
func (user *User) Disable(id int) (int64, error) {
|
|
||||||
return user.Update(id, CommonMap{"status": Disabled})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 激活
|
|
||||||
func (user *User) Enable(id int) (int64, error) {
|
|
||||||
return user.Update(id, CommonMap{"status": Enabled})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证用户名和密码
|
|
||||||
func (user *User) Match(username, password string) bool {
|
|
||||||
where := "(name = ? OR email = ?)"
|
|
||||||
_, err := Db.Where(where, username, username).Get(user)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
hashPassword := user.encryptPassword(password, user.Salt)
|
|
||||||
if hashPassword != user.Password {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户名是否存在
|
|
||||||
func (user *User) UsernameExists(username string) (int64, error) {
|
|
||||||
return Db.Where("name = ?", username).Count(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 邮箱地址是否存在
|
|
||||||
func (user *User) EmailExists(email string) (int64, error) {
|
|
||||||
return Db.Where("email = ?", email).Count(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) List() ([]User, error) {
|
|
||||||
list := make([]User, 0)
|
|
||||||
err := Db.Desc("id").Find(&list)
|
|
||||||
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) Total() (int64, error) {
|
|
||||||
return Db.Count(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 密码加密
|
|
||||||
func (user *User) encryptPassword(password, salt string) string {
|
|
||||||
return utils.Md5(password + salt)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成密码盐值
|
|
||||||
func (user *User) generateSalt() string {
|
|
||||||
return utils.RandString(PasswordSaltLength)
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
AppDir string // 应用根目录
|
|
||||||
ConfDir string // 配置目录
|
|
||||||
LogDir string // 日志目录
|
|
||||||
DataDir string // 存放session等
|
|
||||||
AppConfig string // 应用配置文件
|
|
||||||
Installed bool // 应用是否安装过
|
|
||||||
Setting *ini.Section // 应用配置
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
func InitEnv() {
|
|
||||||
logger.InitLogger()
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatal(err)
|
|
||||||
}
|
|
||||||
AppDir = wd
|
|
||||||
ConfDir = AppDir + "/conf"
|
|
||||||
LogDir = AppDir + "/log"
|
|
||||||
DataDir = AppDir + "/data"
|
|
||||||
AppConfig = ConfDir + "/app.ini"
|
|
||||||
checkDirExists(ConfDir, LogDir, DataDir)
|
|
||||||
Installed = IsInstalled()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断应用是否安装过
|
|
||||||
func IsInstalled() bool {
|
|
||||||
_, err := os.Stat(ConfDir + "/install.lock")
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建安装锁文件
|
|
||||||
func CreateInstallLock() error {
|
|
||||||
_, err := os.Create(ConfDir + "/install.lock")
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("创建安装锁文件conf/install.lock失败")
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测目录是否存在
|
|
||||||
func checkDirExists(path ...string) {
|
|
||||||
for _, value := range path {
|
|
||||||
if !utils.FileExist(value) {
|
|
||||||
logger.Fatal(value + "目录不存在或无权限访问")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
package httpclient
|
|
||||||
|
|
||||||
// http-client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
"fmt"
|
|
||||||
"bytes"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ResponseWrapper struct {
|
|
||||||
StatusCode int
|
|
||||||
Body string
|
|
||||||
Header http.Header
|
|
||||||
}
|
|
||||||
|
|
||||||
func Get(url string, timeout int) ResponseWrapper {
|
|
||||||
req, err := http.NewRequest("GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return createRequestError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return request(req, timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostParams(url string,params string, timeout int) ResponseWrapper {
|
|
||||||
buf := bytes.NewBufferString(params)
|
|
||||||
req, err := http.NewRequest("POST", url, buf)
|
|
||||||
if err != nil {
|
|
||||||
return createRequestError(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
return request(req, timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PostJson(url string, body string, timeout int) ResponseWrapper {
|
|
||||||
buf := bytes.NewBufferString(body)
|
|
||||||
req, err := http.NewRequest("POST", url, buf)
|
|
||||||
if err != nil {
|
|
||||||
return createRequestError(err)
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-type", "application/json")
|
|
||||||
|
|
||||||
return request(req, timeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
func request(req *http.Request, timeout int) ResponseWrapper {
|
|
||||||
wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)}
|
|
||||||
client := &http.Client{}
|
|
||||||
if timeout > 0 {
|
|
||||||
client.Timeout = time.Duration(timeout) * time.Second
|
|
||||||
}
|
|
||||||
setRequestHeader(req)
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error())
|
|
||||||
return wrapper
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error())
|
|
||||||
return wrapper
|
|
||||||
}
|
|
||||||
wrapper.StatusCode = resp.StatusCode
|
|
||||||
wrapper.Body = string(body)
|
|
||||||
wrapper.Header = resp.Header
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
}
|
|
||||||
|
|
||||||
func setRequestHeader(req *http.Request) {
|
|
||||||
req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
|
|
||||||
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron")
|
|
||||||
}
|
|
||||||
|
|
||||||
func createRequestError(err error) ResponseWrapper {
|
|
||||||
errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error())
|
|
||||||
return ResponseWrapper{0, errorMessage, make(http.Header)}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/cihub/seelog"
|
|
||||||
"gopkg.in/macaron.v1"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 日志库
|
|
||||||
|
|
||||||
type Level int8
|
|
||||||
|
|
||||||
var logger seelog.LoggerInterface
|
|
||||||
|
|
||||||
const (
|
|
||||||
DEBUG = iota
|
|
||||||
INFO
|
|
||||||
WARN
|
|
||||||
ERROR
|
|
||||||
FATAL
|
|
||||||
)
|
|
||||||
|
|
||||||
func InitLogger() {
|
|
||||||
config := getLogConfig()
|
|
||||||
l, err := seelog.LoggerFromConfigAsString(config)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
logger = l
|
|
||||||
}
|
|
||||||
|
|
||||||
func Debug(v ...interface{}) {
|
|
||||||
if macaron.Env != macaron.DEV {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
write(DEBUG, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Debugf(format string, v ...interface{}) {
|
|
||||||
if macaron.Env != macaron.DEV {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writef(DEBUG, format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Info(v ...interface{}) {
|
|
||||||
write(INFO, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Infof(format string, v ...interface{}) {
|
|
||||||
writef(INFO, format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Warn(v ...interface{}) {
|
|
||||||
write(WARN, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Warnf(format string, v ...interface{}) {
|
|
||||||
writef(WARN, format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Error(v ...interface{}) {
|
|
||||||
write(ERROR, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Errorf(format string, v ...interface{}) {
|
|
||||||
writef(ERROR, format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fatal(v ...interface{}) {
|
|
||||||
write(FATAL, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Fatalf(format string, v ...interface{}) {
|
|
||||||
writef(FATAL, format, v...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func write(level Level, v ...interface{}) {
|
|
||||||
defer logger.Flush()
|
|
||||||
|
|
||||||
content := ""
|
|
||||||
if macaron.Env == macaron.DEV {
|
|
||||||
pc, file, line, ok := runtime.Caller(2)
|
|
||||||
if ok {
|
|
||||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch level {
|
|
||||||
case DEBUG:
|
|
||||||
logger.Debug(content, v)
|
|
||||||
case INFO:
|
|
||||||
logger.Info(content, v)
|
|
||||||
case WARN:
|
|
||||||
logger.Warn(content, v)
|
|
||||||
case FATAL:
|
|
||||||
logger.Critical(content, v)
|
|
||||||
os.Exit(1)
|
|
||||||
case ERROR:
|
|
||||||
logger.Error(content, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func writef(level Level, format string, v ...interface{}) {
|
|
||||||
defer logger.Flush()
|
|
||||||
|
|
||||||
content := ""
|
|
||||||
if macaron.Env == macaron.DEV {
|
|
||||||
pc, file, line, ok := runtime.Caller(2)
|
|
||||||
if ok {
|
|
||||||
content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
format = content + format
|
|
||||||
|
|
||||||
switch level {
|
|
||||||
case DEBUG:
|
|
||||||
logger.Debugf(format, v...)
|
|
||||||
case INFO:
|
|
||||||
logger.Infof(format, v...)
|
|
||||||
case WARN:
|
|
||||||
logger.Warnf(format, v...)
|
|
||||||
case FATAL:
|
|
||||||
logger.Criticalf(format, v...)
|
|
||||||
os.Exit(1)
|
|
||||||
case ERROR:
|
|
||||||
logger.Errorf(format, v...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogConfig() string {
|
|
||||||
config := `
|
|
||||||
<seelog>
|
|
||||||
<outputs formatid="main">
|
|
||||||
%s
|
|
||||||
<filter levels="info,critical,error,warn">
|
|
||||||
<file path="log/cron.log" />
|
|
||||||
</filter>
|
|
||||||
</outputs>
|
|
||||||
<formats>
|
|
||||||
<format id="main" format="%%Date/%%Time [%%LEV] %%Msg%%n"/>
|
|
||||||
</formats>
|
|
||||||
</seelog>`
|
|
||||||
|
|
||||||
consoleConfig := ""
|
|
||||||
if macaron.Env == macaron.DEV {
|
|
||||||
consoleConfig =
|
|
||||||
`
|
|
||||||
<filter levels="info,debug,critical,warn,error">
|
|
||||||
<console />
|
|
||||||
</filter>
|
|
||||||
`
|
|
||||||
}
|
|
||||||
config = fmt.Sprintf(config, consoleConfig)
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package notify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ouqiang/gocron/models"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"time"
|
|
||||||
"github.com/go-gomail/gomail"
|
|
||||||
)
|
|
||||||
|
|
||||||
// @author qiang.ou<qingqianludao@gmail.com>
|
|
||||||
// @date 2017/5/1-00:19
|
|
||||||
|
|
||||||
|
|
||||||
type Mail struct {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mail *Mail) Send(msg Message) {
|
|
||||||
model := new(models.Setting)
|
|
||||||
mailSetting, err := model.Mail()
|
|
||||||
logger.Debugf("%+v", mailSetting)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("#mail#从数据库获取mail配置失败", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mailSetting.Host == "" {
|
|
||||||
logger.Error("#mail#Host为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mailSetting.Port == 0 {
|
|
||||||
logger.Error("#mail#Port为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mailSetting.User == "" {
|
|
||||||
logger.Error("#mail#User为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if mailSetting.Password == "" {
|
|
||||||
logger.Error("#mail#Password为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toUsers := mail.getActiveMailUsers(mailSetting, msg)
|
|
||||||
mail.send(mailSetting, toUsers, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) {
|
|
||||||
body := msg["content"].(string)
|
|
||||||
body = strings.Replace(body, "\n", "<br>", -1)
|
|
||||||
gomailMessage := gomail.NewMessage()
|
|
||||||
gomailMessage.SetHeader("From", mailSetting.User)
|
|
||||||
gomailMessage.SetHeader("To", toUsers...)
|
|
||||||
gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知")
|
|
||||||
gomailMessage.SetBody("text/html", body)
|
|
||||||
mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port,
|
|
||||||
mailSetting.User, mailSetting.Password)
|
|
||||||
maxTimes := 3
|
|
||||||
i := 0
|
|
||||||
for i < maxTimes {
|
|
||||||
err := mailer.DialAndSend(gomailMessage)
|
|
||||||
if err == nil {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
if i < maxTimes {
|
|
||||||
logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string {
|
|
||||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
|
||||||
users := []string{}
|
|
||||||
for _, v := range(mailSetting.MailUsers) {
|
|
||||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
|
||||||
users = append(users, v.Email)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package notify
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Message map[string]interface{}
|
|
||||||
|
|
||||||
type Notifiable interface {
|
|
||||||
Send(msg Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
var queue chan Message = make(chan Message, 100)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
go run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 把消息推入队列
|
|
||||||
func Push(msg Message) {
|
|
||||||
queue <- msg
|
|
||||||
}
|
|
||||||
|
|
||||||
func run() {
|
|
||||||
for msg := range queue {
|
|
||||||
// 根据任务配置发送通知
|
|
||||||
taskType, taskTypeOk := msg["task_type"]
|
|
||||||
_, taskReceiverIdOk := msg["task_receiver_id"]
|
|
||||||
_, nameOk := msg["name"]
|
|
||||||
_, outputOk := msg["output"]
|
|
||||||
_, statusOk := msg["status"]
|
|
||||||
if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk {
|
|
||||||
logger.Errorf("#notify#参数不完整#%+v", msg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"])
|
|
||||||
logger.Debugf("%+v", msg)
|
|
||||||
switch(taskType.(int8)) {
|
|
||||||
case 1:
|
|
||||||
// 邮件
|
|
||||||
mail := Mail{}
|
|
||||||
go mail.Send(msg)
|
|
||||||
case 2:
|
|
||||||
// Slack
|
|
||||||
slack := Slack{}
|
|
||||||
go slack.Send(msg)
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
package notify
|
|
||||||
// 发送消息到slack
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/ouqiang/gocron/modules/httpclient"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
"strings"
|
|
||||||
"github.com/ouqiang/gocron/models"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Slack struct {}
|
|
||||||
|
|
||||||
func (slack *Slack) Send(msg Message) {
|
|
||||||
model := new(models.Setting)
|
|
||||||
slackSetting, err := model.Slack()
|
|
||||||
if err != nil {
|
|
||||||
logger.Error("#slack#从数据库获取slack配置失败", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if slackSetting.Url == "" {
|
|
||||||
logger.Error("#slack#webhook-url为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(slackSetting.Channels) == 0 {
|
|
||||||
logger.Error("#slack#channels配置为空")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logger.Debugf("%+v", slackSetting)
|
|
||||||
channels := slack.getActiveSlackChannels(slackSetting, msg)
|
|
||||||
logger.Debugf("%+v", channels)
|
|
||||||
for _, channel := range(channels) {
|
|
||||||
slack.send(msg, slackSetting.Url, channel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slack *Slack) send(msg Message, slackUrl string, channel string) {
|
|
||||||
formatBody := slack.format(msg["content"].(string), channel)
|
|
||||||
timeout := 30
|
|
||||||
maxTimes := 3
|
|
||||||
i := 0
|
|
||||||
for i < maxTimes {
|
|
||||||
resp := httpclient.PostJson(slackUrl, formatBody, timeout)
|
|
||||||
if resp.StatusCode == 200 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
if i < maxTimes {
|
|
||||||
logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string {
|
|
||||||
taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",")
|
|
||||||
channels := []string{}
|
|
||||||
for _, v := range(slackSetting.Channels) {
|
|
||||||
if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) {
|
|
||||||
channels = append(channels, v.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return channels
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化消息内容
|
|
||||||
func (slack *Slack) format(content string, channel string) string {
|
|
||||||
content = utils.EscapeJson(content)
|
|
||||||
specialChars := []string{"&", "<", ">"}
|
|
||||||
replaceChars := []string{"&", "<", ">"}
|
|
||||||
content = utils.ReplaceStrings(content, specialChars, replaceChars)
|
|
||||||
|
|
||||||
return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel)
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
"errors"
|
|
||||||
"github.com/ouqiang/gocron/modules/rpc/grpcpool"
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errUnavailable = errors.New("无法连接远程服务器")
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExecWithRetry(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
|
||||||
tryTimes := 60
|
|
||||||
i := 0
|
|
||||||
for i < tryTimes {
|
|
||||||
output, err := Exec(ip, port, taskReq)
|
|
||||||
if err != errUnavailable {
|
|
||||||
return output, err
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
return "", errUnavailable
|
|
||||||
}
|
|
||||||
|
|
||||||
func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
logger.Error("panic#rpc/client.go:Exec#", err)
|
|
||||||
}
|
|
||||||
} ()
|
|
||||||
addr := fmt.Sprintf("%s:%d", ip, port)
|
|
||||||
conn, err := grpcpool.Pool.Get(addr)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
isConnClosed := false
|
|
||||||
defer func() {
|
|
||||||
if !isConnClosed {
|
|
||||||
grpcpool.Pool.Put(addr, conn)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c := pb.NewTaskClient(conn)
|
|
||||||
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
|
||||||
taskReq.Timeout = 86400
|
|
||||||
}
|
|
||||||
timeout := time.Duration(taskReq.Timeout) * time.Second
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
|
||||||
defer cancel()
|
|
||||||
resp, err := c.Run(ctx, taskReq)
|
|
||||||
if err != nil {
|
|
||||||
return parseGRPCError(err, conn, &isConnClosed)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.Error == "" {
|
|
||||||
return resp.Output, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp.Output, errors.New(resp.Error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) {
|
|
||||||
switch grpc.Code(err) {
|
|
||||||
case codes.Unavailable, codes.Internal:
|
|
||||||
conn.Close()
|
|
||||||
*connClosed = true
|
|
||||||
return "", errUnavailable
|
|
||||||
case codes.DeadlineExceeded:
|
|
||||||
return "", errors.New("执行超时, 强制结束")
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
package grpcpool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/silenceper/pool"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
var (
|
|
||||||
Pool GRPCPool
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidConn = errors.New("invalid connection")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Pool = GRPCPool{
|
|
||||||
make(map[string]pool.Pool),
|
|
||||||
sync.RWMutex{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type GRPCPool struct {
|
|
||||||
// map key格式 ip:port
|
|
||||||
conns map[string]pool.Pool
|
|
||||||
sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
|
||||||
p.RLock()
|
|
||||||
pool, ok := p.conns[addr]
|
|
||||||
p.RUnlock()
|
|
||||||
if !ok {
|
|
||||||
err := p.newCommonPool(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.RLock()
|
|
||||||
pool = p.conns[addr]
|
|
||||||
p.RUnlock()
|
|
||||||
conn, err := pool.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn.(*grpc.ClientConn), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
|
|
||||||
p.RLock()
|
|
||||||
defer p.RUnlock()
|
|
||||||
pool, ok := p.conns[addr]
|
|
||||||
if ok {
|
|
||||||
return pool.Put(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrInvalidConn
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 释放连接池
|
|
||||||
func (p *GRPCPool) Release(addr string) {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
pool, ok := p.conns[addr]
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pool.Release()
|
|
||||||
delete(p.conns, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 释放所有连接池
|
|
||||||
func (p *GRPCPool) ReleaseAll() {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
for _, pool := range(p.conns) {
|
|
||||||
pool.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化底层连接池
|
|
||||||
func (p *GRPCPool) newCommonPool(addr string) (error) {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
commonPool, ok := p.conns[addr]
|
|
||||||
if ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
poolConfig := &pool.PoolConfig{
|
|
||||||
InitialCap: 1,
|
|
||||||
MaxCap: 30,
|
|
||||||
Factory: func() (interface{}, error) {
|
|
||||||
return grpc.Dial(addr, grpc.WithInsecure())
|
|
||||||
},
|
|
||||||
Close: func(v interface{}) error {
|
|
||||||
conn, ok := v.(*grpc.ClientConn)
|
|
||||||
if ok && conn != nil {
|
|
||||||
return conn.Close()
|
|
||||||
}
|
|
||||||
return ErrInvalidConn
|
|
||||||
},
|
|
||||||
IdleTimeout: 3 * time.Minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
commonPool, err := pool.NewChannelPool(poolConfig)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.conns[addr] = commonPool
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"net"
|
|
||||||
"google.golang.org/grpc/grpclog"
|
|
||||||
"google.golang.org/grpc"
|
|
||||||
pb "github.com/ouqiang/gocron/modules/rpc/proto"
|
|
||||||
"github.com/ouqiang/gocron/modules/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server struct {}
|
|
||||||
|
|
||||||
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
grpclog.Println(err)
|
|
||||||
}
|
|
||||||
} ()
|
|
||||||
output, err := utils.ExecShell(ctx, req.Command)
|
|
||||||
resp := new(pb.TaskResponse)
|
|
||||||
resp.Output = output
|
|
||||||
if err != nil {
|
|
||||||
resp.Error = err.Error()
|
|
||||||
} else {
|
|
||||||
resp.Error = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Start(addr string) {
|
|
||||||
defer func() {
|
|
||||||
if err := recover(); err != nil {
|
|
||||||
grpclog.Println("panic", err)
|
|
||||||
}
|
|
||||||
} ()
|
|
||||||
l, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Fatal(err)
|
|
||||||
}
|
|
||||||
s := grpc.NewServer()
|
|
||||||
pb.RegisterTaskServer(s, Server{})
|
|
||||||
grpclog.Println("listen ", addr)
|
|
||||||
err = s.Serve(l)
|
|
||||||
if err != nil {
|
|
||||||
grpclog.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
package setting
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultSection = "default"
|
|
||||||
|
|
||||||
// 读取配置
|
|
||||||
func Read(filename string) (*ini.Section,error) {
|
|
||||||
config, err := ini.Load(filename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
section := config.Section(DefaultSection)
|
|
||||||
|
|
||||||
return section, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入配置
|
|
||||||
func Write(config []string, filename string) error {
|
|
||||||
if len(config) == 0 {
|
|
||||||
return errors.New("参数不能为空")
|
|
||||||
}
|
|
||||||
if len(config) % 2 != 0 {
|
|
||||||
return errors.New("参数不匹配")
|
|
||||||
}
|
|
||||||
|
|
||||||
file := ini.Empty()
|
|
||||||
|
|
||||||
section, err := file.NewSection(DefaultSection)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := 0 ;i < len(config); {
|
|
||||||
_, err = section.NewKey(config[i], config[i+1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i += 2
|
|
||||||
}
|
|
||||||
err = file.SaveTo(filename)
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
package ssh
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HostAuthType int8 // 认证方式
|
|
||||||
|
|
||||||
const (
|
|
||||||
HostPassword = 1 // 密码认证
|
|
||||||
HostPublicKey = 2 // 公钥认证
|
|
||||||
)
|
|
||||||
|
|
||||||
const SSHConnectTimeout = 10
|
|
||||||
|
|
||||||
|
|
||||||
type SSHConfig struct {
|
|
||||||
AuthType HostAuthType
|
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
PrivateKey string
|
|
||||||
Host string
|
|
||||||
Port int
|
|
||||||
ExecTimeout int// 执行超时时间
|
|
||||||
}
|
|
||||||
|
|
||||||
type Result struct {
|
|
||||||
Output string
|
|
||||||
Err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) {
|
|
||||||
timeout := time.Duration(SSHConnectTimeout) * time.Second
|
|
||||||
// 密码认证
|
|
||||||
if sshConfig.AuthType == HostPassword {
|
|
||||||
config = &ssh.ClientConfig{
|
|
||||||
User: sshConfig.User,
|
|
||||||
Auth: []ssh.AuthMethod{
|
|
||||||
ssh.Password(sshConfig.Password),
|
|
||||||
},
|
|
||||||
Timeout: timeout,
|
|
||||||
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey))
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 公钥认证
|
|
||||||
config = &ssh.ClientConfig{
|
|
||||||
User: sshConfig.User,
|
|
||||||
Auth: []ssh.AuthMethod{
|
|
||||||
ssh.PublicKeys(signer),
|
|
||||||
},
|
|
||||||
Timeout: timeout,
|
|
||||||
HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 执行shell命令
|
|
||||||
func Exec(sshConfig SSHConfig, cmd string) (output string, err error) {
|
|
||||||
client, err := getClient(sshConfig)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer client.Close()
|
|
||||||
|
|
||||||
session, err := client.NewSession()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer session.Close()
|
|
||||||
|
|
||||||
// 后台运行
|
|
||||||
if sshConfig.ExecTimeout < 0 {
|
|
||||||
go session.CombinedOutput(cmd)
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
// 不限制超时
|
|
||||||
if sshConfig.ExecTimeout == 0 {
|
|
||||||
outputByte, execErr := session.CombinedOutput(cmd)
|
|
||||||
output = string(outputByte)
|
|
||||||
err = execErr
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var resultChan chan Result = make(chan Result)
|
|
||||||
var timeoutChan chan bool = make(chan bool)
|
|
||||||
go func() {
|
|
||||||
output, err := session.CombinedOutput(cmd)
|
|
||||||
resultChan <- Result{string(output), err}
|
|
||||||
}()
|
|
||||||
// todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性
|
|
||||||
go triggerTimeout(timeoutChan, sshConfig.ExecTimeout)
|
|
||||||
select {
|
|
||||||
case result := <- resultChan:
|
|
||||||
output = result.Output
|
|
||||||
err = result.Err
|
|
||||||
case <- timeoutChan:
|
|
||||||
output = ""
|
|
||||||
err = errors.New("timeout")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func getClient(sshConfig SSHConfig) (*ssh.Client, error) {
|
|
||||||
config, err := parseSSHConfig(sshConfig)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port)
|
|
||||||
|
|
||||||
return ssh.Dial("tcp", addr, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func triggerTimeout(ch chan bool, timeout int){
|
|
||||||
// 最长执行时间不能超过24小时
|
|
||||||
if timeout <= 0 || timeout > 86400 {
|
|
||||||
timeout = 86400
|
|
||||||
}
|
|
||||||
time.Sleep(time.Duration(timeout) * time.Second)
|
|
||||||
close(ch)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/ouqiang/gocron/modules/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// json 格式输出
|
|
||||||
|
|
||||||
type response struct {
|
|
||||||
Code int `json:"code"` // 状态码 0:成功 非0:失败
|
|
||||||
Message string `json:"message"` // 信息
|
|
||||||
Data interface{} `json:"data"` // 数据
|
|
||||||
}
|
|
||||||
|
|
||||||
type JsonResponse struct{}
|
|
||||||
|
|
||||||
const ResponseSuccess = 0
|
|
||||||
const ResponseFailure = 1
|
|
||||||
const NotFound = 2
|
|
||||||
const AuthError = 3
|
|
||||||
const ServerError = 4
|
|
||||||
|
|
||||||
const SuccessContent = "操作成功"
|
|
||||||
const FailureContent = "操作失败"
|
|
||||||
|
|
||||||
func JsonResponseByErr(err error) string {
|
|
||||||
json := JsonResponse{}
|
|
||||||
if err != nil {
|
|
||||||
return json.CommonFailure(FailureContent, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return json.Success(SuccessContent, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JsonResponse) Success(message string, data interface{}) string {
|
|
||||||
return j.response(ResponseSuccess, message, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JsonResponse) Failure(code int, message string) string {
|
|
||||||
return j.response(code, message, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JsonResponse) CommonFailure(message string, err... error) string {
|
|
||||||
if len(err) > 0 {
|
|
||||||
logger.Warn(err)
|
|
||||||
}
|
|
||||||
return j.Failure(ResponseFailure, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (j *JsonResponse) response(code int, message string, data interface{}) string {
|
|
||||||
resp := response{
|
|
||||||
Code: code,
|
|
||||||
Message: message,
|
|
||||||
Data: data,
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := json.Marshal(resp)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(result)
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/hex"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
"runtime"
|
|
||||||
"github.com/Tang-RoseChild/mahonia"
|
|
||||||
"strings"
|
|
||||||
"os"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// 生成长度为length的随机字符串
|
|
||||||
func RandString(length int64) string {
|
|
||||||
sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
|
||||||
result := []byte{}
|
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
sourceLength := len(sources)
|
|
||||||
var i int64 = 0
|
|
||||||
for ; i < length; i++ {
|
|
||||||
result = append(result, sources[r.Intn(sourceLength)])
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成32位MD5摘要
|
|
||||||
func Md5(str string) string {
|
|
||||||
m := md5.New()
|
|
||||||
m.Write([]byte(str))
|
|
||||||
|
|
||||||
return hex.EncodeToString(m.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成0-max之间随机数
|
|
||||||
func RandNumber(max int) int {
|
|
||||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
|
|
||||||
return r.Intn(max)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断当前系统是否是windows
|
|
||||||
func IsWindows() bool {
|
|
||||||
return runtime.GOOS == "windows"
|
|
||||||
}
|
|
||||||
|
|
||||||
// GBK编码转换为UTF8
|
|
||||||
func GBK2UTF8(s string) (string, bool) {
|
|
||||||
dec := mahonia.NewDecoder("gbk")
|
|
||||||
|
|
||||||
return dec.ConvertStringOK(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量替换字符串
|
|
||||||
func ReplaceStrings(s string, old []string, replace []string) string {
|
|
||||||
if s == "" {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
if len(old) != len(replace) {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, v := range old {
|
|
||||||
s = strings.Replace(s, v, replace[i], 1000)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func InStringSlice(slice []string, element string) bool {
|
|
||||||
element = strings.TrimSpace(element)
|
|
||||||
for _, v := range slice {
|
|
||||||
if strings.TrimSpace(v) == element{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转义json特殊字符
|
|
||||||
func EscapeJson(s string) string {
|
|
||||||
specialChars := []string{"\\", "\b","\f", "\n", "\r", "\t", "\"",}
|
|
||||||
replaceChars := []string{ "\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\"",}
|
|
||||||
|
|
||||||
return ReplaceStrings(s, specialChars, replaceChars)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断文件是否存在及是否有权限访问
|
|
||||||
func FileExist(file string) bool {
|
|
||||||
_, err := os.Stat(file)
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if os.IsPermission(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化环境变量
|
|
||||||
func FormatUnixEnv(key, value string) string {
|
|
||||||
return fmt.Sprintf("export %s=%s; ", key, value)
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package utils
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestRandString(t *testing.T) {
|
|
||||||
str := RandString(32)
|
|
||||||
if len(str) != 32 {
|
|
||||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMd5(t *testing.T) {
|
|
||||||
str := Md5("123456")
|
|
||||||
if len(str) != 32 {
|
|
||||||
t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRandNumber(t *testing.T) {
|
|
||||||
num := RandNumber(10000)
|
|
||||||
if num <= 0 && num >= 10000 {
|
|
||||||
t.Fatalf("随机数不在有效范围内-%d", num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os/exec"
|
|
||||||
"syscall"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Result struct {
|
|
||||||
output string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行shell命令,可设置执行超时时间
|
|
||||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
|
||||||
cmd := exec.Command("/bin/bash", "-c", command)
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
Setpgid: true,
|
|
||||||
}
|
|
||||||
var resultChan chan Result = make(chan Result)
|
|
||||||
go func() {
|
|
||||||
output ,err := cmd.CombinedOutput()
|
|
||||||
resultChan <- Result{string(output), err}
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <- ctx.Done():
|
|
||||||
if cmd.Process.Pid > 0 {
|
|
||||||
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
|
||||||
}
|
|
||||||
return "", errors.New("timeout killed")
|
|
||||||
case result := <- resultChan:
|
|
||||||
return result.output, result.err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"os/exec"
|
|
||||||
"strconv"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Result struct {
|
|
||||||
output string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// 执行shell命令,可设置执行超时时间
|
|
||||||
func ExecShell(ctx context.Context, command string) (string, error) {
|
|
||||||
cmd := exec.Command("cmd", "/C", command)
|
|
||||||
// 隐藏cmd窗口
|
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
|
||||||
HideWindow: true,
|
|
||||||
}
|
|
||||||
var resultChan chan Result = make(chan Result)
|
|
||||||
go func() {
|
|
||||||
output ,err := cmd.CombinedOutput()
|
|
||||||
resultChan <- Result{string(output), err}
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <- ctx.Done():
|
|
||||||
if cmd.Process.Pid > 0 {
|
|
||||||
exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run()
|
|
||||||
cmd.Process.Kill()
|
|
||||||
}
|
|
||||||
return "", errors.New("timeout killed")
|
|
||||||
case result := <- resultChan:
|
|
||||||
return ConvertEncoding(result.output), result.err
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ConvertEncoding(outputGBK string) (string) {
|
|
||||||
// windows平台编码为gbk,需转换为utf8才能入库
|
|
||||||
outputUTF8, ok := GBK2UTF8(outputGBK)
|
|
||||||
if ok {
|
|
||||||
return outputUTF8
|
|
||||||
}
|
|
||||||
|
|
||||||
return "命令输出转换编码失败(gbk to utf8)"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,216 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# 生成压缩包 xx.tar.gz或xx.zip
|
||||||
|
# 使用 ./package.sh -a amd664 -p linux -v v2.0.0
|
||||||
|
|
||||||
|
# 任何命令返回非0值退出
|
||||||
|
set -o errexit
|
||||||
|
# 使用未定义的变量退出
|
||||||
|
set -o nounset
|
||||||
|
# 管道中任一命令执行失败退出
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
eval $(go env)
|
||||||
|
|
||||||
|
# 二进制文件名
|
||||||
|
BINARY_NAME=''
|
||||||
|
# main函数所在文件
|
||||||
|
MAIN_FILE=""
|
||||||
|
|
||||||
|
# 提取git最新tag作为应用版本
|
||||||
|
VERSION=''
|
||||||
|
# 最新git commit id
|
||||||
|
GIT_COMMIT_ID=''
|
||||||
|
|
||||||
|
# 外部输入的系统
|
||||||
|
INPUT_OS=()
|
||||||
|
# 外部输入的架构
|
||||||
|
INPUT_ARCH=()
|
||||||
|
# 未指定OS,默认值
|
||||||
|
DEFAULT_OS=${GOHOSTOS}
|
||||||
|
# 未指定ARCH,默认值
|
||||||
|
DEFAULT_ARCH=${GOHOSTARCH}
|
||||||
|
# 支持的系统
|
||||||
|
SUPPORT_OS=(linux darwin windows)
|
||||||
|
# 支持的架构
|
||||||
|
SUPPORT_ARCH=(386 amd64)
|
||||||
|
|
||||||
|
# 编译参数
|
||||||
|
LDFLAGS=''
|
||||||
|
# 需要打包的文件
|
||||||
|
INCLUDE_FILE=()
|
||||||
|
# 打包文件生成目录
|
||||||
|
PACKAGE_DIR=''
|
||||||
|
# 编译文件生成目录
|
||||||
|
BUILD_DIR=''
|
||||||
|
|
||||||
|
# 获取git 最新tag name
|
||||||
|
git_latest_tag() {
|
||||||
|
local COMMIT_ID=""
|
||||||
|
local TAG_NAME=""
|
||||||
|
COMMIT_ID=`git rev-list --tags --max-count=1`
|
||||||
|
TAG_NAME=`git describe --tags "${COMMIT_ID}"`
|
||||||
|
|
||||||
|
echo ${TAG_NAME}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 获取git 最新commit id
|
||||||
|
git_latest_commit() {
|
||||||
|
echo "$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印信息
|
||||||
|
print_message() {
|
||||||
|
echo "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打印信息后推出
|
||||||
|
print_message_and_exit() {
|
||||||
|
if [[ -n $1 ]]; then
|
||||||
|
print_message "$1"
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# 设置系统、CPU架构
|
||||||
|
set_os_arch() {
|
||||||
|
if [[ ${#INPUT_OS[@]} = 0 ]];then
|
||||||
|
INPUT_OS=("${DEFAULT_OS}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#INPUT_ARCH[@]} = 0 ]];then
|
||||||
|
INPUT_ARCH=("${DEFAULT_ARCH}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for OS in "${INPUT_OS[@]}"; do
|
||||||
|
if [[ ! "${SUPPORT_OS[*]}" =~ ${OS} ]]; then
|
||||||
|
print_message_and_exit "不支持的系统${OS}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for ARCH in "${INPUT_ARCH[@]}";do
|
||||||
|
if [[ ! "${SUPPORT_ARCH[*]}" =~ ${ARCH} ]]; then
|
||||||
|
print_message_and_exit "不支持的CPU架构${ARCH}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 初始化
|
||||||
|
init() {
|
||||||
|
set_os_arch
|
||||||
|
|
||||||
|
if [[ -z "${VERSION}" ]];then
|
||||||
|
VERSION=`git_latest_tag`
|
||||||
|
fi
|
||||||
|
GIT_COMMIT_ID=`git_latest_commit`
|
||||||
|
LDFLAGS="-w -X 'main.AppVersion=${VERSION}' -X 'main.BuildDate=`date '+%Y-%m-%d %H:%M:%S'`' -X 'main.GitCommit=${GIT_COMMIT_ID}'"
|
||||||
|
|
||||||
|
PACKAGE_DIR=${BINARY_NAME}-package
|
||||||
|
BUILD_DIR=${BINARY_NAME}-build
|
||||||
|
|
||||||
|
if [[ -d ${BUILD_DIR} ]];then
|
||||||
|
rm -rf ${BUILD_DIR}
|
||||||
|
fi
|
||||||
|
if [[ -d ${PACKAGE_DIR} ]];then
|
||||||
|
rm -rf ${PACKAGE_DIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p ${BUILD_DIR}
|
||||||
|
mkdir -p ${PACKAGE_DIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 编译
|
||||||
|
build() {
|
||||||
|
local FILENAME=''
|
||||||
|
for OS in "${INPUT_OS[@]}";do
|
||||||
|
for ARCH in "${INPUT_ARCH[@]}";do
|
||||||
|
if [[ "${OS}" = "windows" ]];then
|
||||||
|
FILENAME=${BINARY_NAME}.exe
|
||||||
|
else
|
||||||
|
FILENAME=${BINARY_NAME}
|
||||||
|
fi
|
||||||
|
env CGO_ENABLED=0 GOOS=${OS} GOARCH=${ARCH} go build -ldflags "${LDFLAGS}" -o ${BUILD_DIR}/${BINARY_NAME}-${OS}-${ARCH}/${FILENAME} ${MAIN_FILE}
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打包
|
||||||
|
package_binary() {
|
||||||
|
cd ${BUILD_DIR}
|
||||||
|
|
||||||
|
for OS in "${INPUT_OS[@]}";do
|
||||||
|
for ARCH in "${INPUT_ARCH[@]}";do
|
||||||
|
package_file ${BINARY_NAME}-${OS}-${ARCH}
|
||||||
|
if [[ "${OS}" = "windows" ]];then
|
||||||
|
zip -rq ../${PACKAGE_DIR}/${BINARY_NAME}-${VERSION}-${OS}-${ARCH}.zip ${BINARY_NAME}-${OS}-${ARCH}
|
||||||
|
else
|
||||||
|
tar czf ../${PACKAGE_DIR}/${BINARY_NAME}-${VERSION}-${OS}-${ARCH}.tar.gz ${BINARY_NAME}-${OS}-${ARCH}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
cd ${OLDPWD}
|
||||||
|
}
|
||||||
|
|
||||||
|
# 打包文件
|
||||||
|
package_file() {
|
||||||
|
if [[ "${#INCLUDE_FILE[@]}" = "0" ]];then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
for item in "${INCLUDE_FILE[@]}"; do
|
||||||
|
cp -r ../${item} $1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# 清理
|
||||||
|
clean() {
|
||||||
|
if [[ -d ${BUILD_DIR} ]];then
|
||||||
|
rm -rf ${BUILD_DIR}
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 运行
|
||||||
|
run() {
|
||||||
|
init
|
||||||
|
build
|
||||||
|
package_binary
|
||||||
|
clean
|
||||||
|
}
|
||||||
|
|
||||||
|
package_gocron() {
|
||||||
|
BINARY_NAME='gocron'
|
||||||
|
MAIN_FILE="./cmd/gocron/gocron.go"
|
||||||
|
INCLUDE_FILE=()
|
||||||
|
|
||||||
|
|
||||||
|
run
|
||||||
|
}
|
||||||
|
|
||||||
|
package_gocron_node() {
|
||||||
|
BINARY_NAME='gocron-node'
|
||||||
|
MAIN_FILE="./cmd/node/node.go"
|
||||||
|
INCLUDE_FILE=()
|
||||||
|
|
||||||
|
run
|
||||||
|
}
|
||||||
|
|
||||||
|
# p 平台 linux darwin windows
|
||||||
|
# a 架构 386 amd64
|
||||||
|
# v 版本号 默认取git最新tag
|
||||||
|
while getopts "p:a:v:" OPT;
|
||||||
|
do
|
||||||
|
case ${OPT} in
|
||||||
|
p) IPS=',' read -r -a INPUT_OS <<< "${OPTARG}"
|
||||||
|
;;
|
||||||
|
a) IPS=',' read -r -a INPUT_ARCH <<< "${OPTARG}"
|
||||||
|
;;
|
||||||
|
v) VERSION=$OPTARG
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
package_gocron
|
||||||
|
package_gocron_node
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,22 +0,0 @@
|
|||||||
div.tagsinput { border: 1px solid rgba(0, 0, 0, 0.15); background: #FFF; padding:5px; width:300px; height:100px; overflow-y: auto; border-radius: 0.3125em;-webkit-box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;-webkit-appearance: none;}
|
|
||||||
div.tagsinput span.tag { border: 1px solid #a5d24a; -moz-border-radius:2px; -webkit-border-radius:2px; display: block; float: left; padding: 5px; text-decoration:none; background: #cde69c; color: #638421; margin-right: 5px; margin-bottom:5px;font-family: helvetica; font-size:13px;}
|
|
||||||
div.tagsinput span.tag a { font-weight: bold; color: #82ad2b; text-decoration:none; font-size: 11px; }
|
|
||||||
div.tagsinput input { width:120px !important; margin:0px;font-size: 13px; border:1px solid transparent; padding:5px; background: transparent; color: #000; outline:0px; margin-right:5px; margin-bottom:5px;
|
|
||||||
-webkit-transition: background-color 0.3s ease-out, -webkit-box-shadow 0.2s ease, border-color 0.2s ease;
|
|
||||||
transition: background-color 0.3s ease-out, box-shadow 0.2s ease, border-color 0.2s ease;
|
|
||||||
-webkit-box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
|
|
||||||
box-shadow: 0em 0em 0em 0em rgba(0, 0, 0, 0.3) inset;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-webkit-tap-highlight-color: rgba(255, 255, 255, 0);
|
|
||||||
}
|
|
||||||
div.tagsinput input:focus {
|
|
||||||
color: rgba(0, 0, 0, 0.85) !important;
|
|
||||||
border-bottom-left-radius: 0;
|
|
||||||
border-top-left-radius: 0;
|
|
||||||
-webkit-appearance: none;
|
|
||||||
-webkit-box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
|
|
||||||
box-shadow: 0.3em 0em 0em 0em rgba(0, 0, 0, 0.2) inset;
|
|
||||||
}
|
|
||||||
div.tagsinput div { display:block; float: left; }
|
|
||||||
.tags_clear { clear: both; width: 100%; height: 0px; }
|
|
||||||
.not_valid {background: #FBD8DB !important; color: #90111A !important;}
|
|
||||||
@@ -1,418 +0,0 @@
|
|||||||
/*framework edit*/
|
|
||||||
html{
|
|
||||||
height:100%;
|
|
||||||
font-size:100%;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font: 14px 'Helvetica Neue','Microsoft YaHei','Hiragino Sans GB','WenQuanYi Micro Hei',Helvetica,Arial,Sans-serif;
|
|
||||||
line-height: 1.428571429;
|
|
||||||
color: #444;
|
|
||||||
margin:0px;
|
|
||||||
padding:0px;
|
|
||||||
height:100%;
|
|
||||||
}
|
|
||||||
ul,li{
|
|
||||||
list-style:none;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
text-decoration:none;
|
|
||||||
color:#666;
|
|
||||||
}
|
|
||||||
.ui.dropdown > .text {
|
|
||||||
min-width:150px;
|
|
||||||
}
|
|
||||||
.ui.notif.menu {
|
|
||||||
box-shadow:none;
|
|
||||||
}
|
|
||||||
.ui.notif.menu .item {
|
|
||||||
padding:0 10px 0 0;
|
|
||||||
margin-right:16px
|
|
||||||
}
|
|
||||||
.ui.notif.menu .item:before{
|
|
||||||
display:none;
|
|
||||||
}
|
|
||||||
.ui.notif.menu .item:hover{
|
|
||||||
color:#666;
|
|
||||||
}
|
|
||||||
/*the style begin*/
|
|
||||||
.page{
|
|
||||||
min-height:100%;
|
|
||||||
width:100%;
|
|
||||||
min-width:1200px;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 0px 4px 15px #181818;
|
|
||||||
padding-bottom:16px;
|
|
||||||
padding-bottom:1rem;
|
|
||||||
}
|
|
||||||
span.ui.checkbox {
|
|
||||||
padding-right:16px;
|
|
||||||
}
|
|
||||||
.ui.radio.checkbox{
|
|
||||||
margin:16px 16px 8px 0 !important;
|
|
||||||
}
|
|
||||||
.ui.warning.message a{
|
|
||||||
color:#96904D;
|
|
||||||
}
|
|
||||||
.ui.warning.message a:hover, .ui.warning.message a:focus {
|
|
||||||
text-decoration:underline;
|
|
||||||
}
|
|
||||||
.ui.feed a{
|
|
||||||
color: #009FDA;
|
|
||||||
}
|
|
||||||
.ui.feed a:hover,.ui.feed a:focus{
|
|
||||||
color:#00BAFF;
|
|
||||||
text-decoration:underline;
|
|
||||||
}
|
|
||||||
.ui.table {
|
|
||||||
font-size:14px;
|
|
||||||
}
|
|
||||||
/*select*/
|
|
||||||
select {
|
|
||||||
text-transform: none;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
.form-control {
|
|
||||||
height: 34px;
|
|
||||||
padding: 6px 12px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.428571429;
|
|
||||||
vertical-align: middle;
|
|
||||||
color: rgba(0,0,0,.8);
|
|
||||||
background-color: #ffffff;
|
|
||||||
background-image: none;
|
|
||||||
border: 1px solid #cccccc;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
.form-control:focus {
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
.form-control:-moz-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.form-control::-moz-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
.form-control:-ms-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
.form-control::-webkit-input-placeholder {
|
|
||||||
color: #999999;
|
|
||||||
}
|
|
||||||
/*segment*/
|
|
||||||
.ui.segment .right{
|
|
||||||
float:right;
|
|
||||||
}
|
|
||||||
/*header*/
|
|
||||||
header{
|
|
||||||
position:relative;
|
|
||||||
height:72px;
|
|
||||||
}
|
|
||||||
#logo{
|
|
||||||
float:left;
|
|
||||||
width:250px;
|
|
||||||
height:72px;
|
|
||||||
padding-top:15px;
|
|
||||||
font-size:30px;
|
|
||||||
}
|
|
||||||
#logo a{
|
|
||||||
display:block;
|
|
||||||
width:180px;
|
|
||||||
height:72px;
|
|
||||||
text-indent: -999em;
|
|
||||||
}
|
|
||||||
header .user{
|
|
||||||
display:inline;
|
|
||||||
float:right;
|
|
||||||
margin-top:22px;
|
|
||||||
}
|
|
||||||
/*main content*/
|
|
||||||
.pageHeader, .verticalMenu{
|
|
||||||
padding:16px 0;
|
|
||||||
padding:1rem 0;
|
|
||||||
}
|
|
||||||
/*show api key*/
|
|
||||||
#showApiKey .segment{
|
|
||||||
background:none;
|
|
||||||
box-shadow:none;
|
|
||||||
-webkit-box-shadow:none;
|
|
||||||
}
|
|
||||||
#showApiKey .segment label{
|
|
||||||
font-size:16px;
|
|
||||||
}
|
|
||||||
#deviceQuickButton {
|
|
||||||
float:right;
|
|
||||||
}
|
|
||||||
/*index list*/
|
|
||||||
#indexList{
|
|
||||||
padding-top:2rem;
|
|
||||||
}
|
|
||||||
#indexList .segment{
|
|
||||||
padding:0;
|
|
||||||
}
|
|
||||||
#indexList a.content{
|
|
||||||
position:relative;
|
|
||||||
padding:1rem;
|
|
||||||
display:block;
|
|
||||||
min-height:13em;
|
|
||||||
z-index:100;
|
|
||||||
}
|
|
||||||
#indexList a.content:hover, #indexList a.content:focus{
|
|
||||||
background-color:#b4ddf5 !important;
|
|
||||||
}
|
|
||||||
#indexList h4{
|
|
||||||
margin:0;
|
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
#indexList p.deviceId{
|
|
||||||
margin-top:0;
|
|
||||||
}
|
|
||||||
#indexList p.deviceDescription{
|
|
||||||
height:84px;
|
|
||||||
overflow:auto;
|
|
||||||
margin:2rem 0;
|
|
||||||
}
|
|
||||||
#indexList h4.newDevice{
|
|
||||||
padding:4.7em 0;
|
|
||||||
text-align:center;
|
|
||||||
top:50%;
|
|
||||||
}
|
|
||||||
#indexList h4.newDevice i.icon{
|
|
||||||
display:initial;
|
|
||||||
}
|
|
||||||
#indexList p{
|
|
||||||
margin:0;
|
|
||||||
font-size:14px;
|
|
||||||
font-size:0.875rem;
|
|
||||||
}
|
|
||||||
/*the device list*/
|
|
||||||
.ui.device.segment{
|
|
||||||
clear: both !important;
|
|
||||||
}
|
|
||||||
.ui.device.segment p{
|
|
||||||
color:#666;
|
|
||||||
font-size:14px;
|
|
||||||
font-size:0.875rem;
|
|
||||||
margin:4px 0;
|
|
||||||
margin:0.25rem 0;
|
|
||||||
}
|
|
||||||
.ui.device.segment span.stress{
|
|
||||||
font-size:16px;
|
|
||||||
font-size:1rem;
|
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
.ui.device.segment .verborder{
|
|
||||||
border-left: 3px solid #007ad5;
|
|
||||||
}
|
|
||||||
.ui.device.segment p.sensorStatus{
|
|
||||||
font-size:14px;
|
|
||||||
font-size:0.875rem;
|
|
||||||
}
|
|
||||||
.ui.device.segment .sub.header{
|
|
||||||
line-height:1.4;
|
|
||||||
}
|
|
||||||
/*new device*/
|
|
||||||
.locmap {
|
|
||||||
height: 180px;
|
|
||||||
background-color: #CCC;
|
|
||||||
text-align: center;
|
|
||||||
width: 100% !important;
|
|
||||||
border: #CCC 1px solid;z
|
|
||||||
}
|
|
||||||
#location label{
|
|
||||||
margin: 0em 0em 0.3em;
|
|
||||||
display: block;
|
|
||||||
color: #555555;
|
|
||||||
font-size:14px;
|
|
||||||
font-size:0.875rem;
|
|
||||||
}
|
|
||||||
#location input{
|
|
||||||
padding:0!important;
|
|
||||||
border: 1px solid transparent!important;
|
|
||||||
box-shadow:none!important;
|
|
||||||
-webkit-box-shadow:none!important;
|
|
||||||
}
|
|
||||||
/*the device manage page*/
|
|
||||||
.ui.small.header{
|
|
||||||
font-weight:normal;
|
|
||||||
}
|
|
||||||
.ui.grid.segment.sensor.segment{
|
|
||||||
padding:32px 0 16px;
|
|
||||||
padding:2rem 0 1rem;
|
|
||||||
border-bottom:1px solid rgba(0,0,0,.1);
|
|
||||||
}
|
|
||||||
.ui.device.segment a.externalUrl {
|
|
||||||
color:#007ad5;
|
|
||||||
}
|
|
||||||
.ui.device.segment .sensorAction{
|
|
||||||
margin-top:8px;
|
|
||||||
margin-top:0.5rem;
|
|
||||||
}
|
|
||||||
.ui.device.segment a.sensorEdit{
|
|
||||||
margin-right:14px;
|
|
||||||
margin-right:0.875rem;
|
|
||||||
}
|
|
||||||
.ui.device.segment a.sensorEdit:hover, .ui.basic.attached.accordion .content a:hover,.ui.device.segment a.sensorEdit:focus, .ui.basic.attached.accordion .content a:focus{
|
|
||||||
color:#333;
|
|
||||||
}
|
|
||||||
.ui.device.segment a:hover, .ui.device.segment a:focus{
|
|
||||||
cursor:pointer;
|
|
||||||
}
|
|
||||||
.ui.device.segment a.externalUrl:hover, .ui.device.segment a.externalUrl:focus{
|
|
||||||
text-decoration:underline;
|
|
||||||
}
|
|
||||||
.ui.vertical.center.segment{
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
.ui.data.segment{
|
|
||||||
background: transparent;
|
|
||||||
box-shadow: none;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
line-height:1.2;
|
|
||||||
text-align:center;
|
|
||||||
margin-top:22px !important;
|
|
||||||
}
|
|
||||||
.ui.data.segment p.data{
|
|
||||||
font-size:48px;
|
|
||||||
font-size:3rem;
|
|
||||||
}
|
|
||||||
.ui.data.segment p.gps{
|
|
||||||
font-size:32px;
|
|
||||||
font-size:2rem;
|
|
||||||
}
|
|
||||||
.ui.data.segment p.unit{
|
|
||||||
font-size:12px;
|
|
||||||
font-size:0.75rem;
|
|
||||||
margin:0;
|
|
||||||
}
|
|
||||||
.ui.info.segment{
|
|
||||||
padding-bottom:8px;
|
|
||||||
padding-bottom:0.5rem;
|
|
||||||
}
|
|
||||||
/* ui header*/
|
|
||||||
h5.ui.header.sprite-icon {
|
|
||||||
padding-left:22px;
|
|
||||||
background:url(../images/icon-sprite.png) no-repeat top left;
|
|
||||||
}
|
|
||||||
h5.ui.header.sprite-icon.data{ background-position: 0 -68px;}
|
|
||||||
h5.ui.header.sprite-icon.bird{ background-position: 0 0;}
|
|
||||||
h5.ui.header.sprite-icon.gps{ background-position: 0 -136px;}
|
|
||||||
h5.ui.header.sprite-icon.key{ background-position: 0 -204px; }
|
|
||||||
h5.ui.header.sprite-icon.pic{ background-position: 0 -268px;}
|
|
||||||
h5.ui.header.sprite-icon.switchicon{ background-position: 0 -336px;}
|
|
||||||
/*label*/
|
|
||||||
.ui.attached.label.show{
|
|
||||||
z-index:99;
|
|
||||||
background: transparent;
|
|
||||||
color:#333;
|
|
||||||
right:14px !important;
|
|
||||||
top:8px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
border-radius: 0!important;
|
|
||||||
}
|
|
||||||
.ui.attached.label.show.active{
|
|
||||||
background:#fff;
|
|
||||||
color: #333;
|
|
||||||
border-style:solid;
|
|
||||||
border-color:#ccc;
|
|
||||||
border-width:1px 1px 0px;
|
|
||||||
z-index:9999;
|
|
||||||
}
|
|
||||||
.ui.type.label{
|
|
||||||
background-color:#47a447;
|
|
||||||
border-color:#47a447;
|
|
||||||
color:#fff;`
|
|
||||||
font-size:12px;
|
|
||||||
font-size:0.75rem;
|
|
||||||
padding:0 7px!important;
|
|
||||||
border-radius:.325em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
.ui.label.error{
|
|
||||||
border-color:#D24B4C;
|
|
||||||
color:#fff;
|
|
||||||
}
|
|
||||||
/*accordion*/
|
|
||||||
.ui.basic.attached.accordion{
|
|
||||||
width:inherit;
|
|
||||||
padding:0 28px 14px;
|
|
||||||
}
|
|
||||||
.ui.basic.attached.accordion.showChart .title{
|
|
||||||
font-size:12px;
|
|
||||||
font-size:0.75rem;
|
|
||||||
position:absolute;
|
|
||||||
right:32px;
|
|
||||||
z-index:999;
|
|
||||||
}
|
|
||||||
.ui.basic.attached.accordion.action .title{
|
|
||||||
margin:0;
|
|
||||||
padding-top:0;
|
|
||||||
}
|
|
||||||
.ui.basic.attached.accordion.action .content{
|
|
||||||
margin-top:8px;
|
|
||||||
/* border-top:1px solid rgba(204, 204, 204, 0.3);*/
|
|
||||||
}
|
|
||||||
/*chart*/
|
|
||||||
.chartShow{
|
|
||||||
position: absolute;
|
|
||||||
top: 38px;
|
|
||||||
right: 14px;
|
|
||||||
width: 96%;
|
|
||||||
height: 100%;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.active.chartShow{
|
|
||||||
display:block;
|
|
||||||
}
|
|
||||||
/*image box*/
|
|
||||||
.field.devimage{
|
|
||||||
overflow-x: scroll;
|
|
||||||
overflow-y: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.dev_img_inner {
|
|
||||||
width:7777px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
/*footer start*/
|
|
||||||
footer {
|
|
||||||
width:100%;
|
|
||||||
height:150px;
|
|
||||||
min-width:1200px;
|
|
||||||
background:#323232;
|
|
||||||
position:absolute;
|
|
||||||
z-index: -20;
|
|
||||||
}
|
|
||||||
#copyrights {
|
|
||||||
margin:0 auto;
|
|
||||||
height:140px;
|
|
||||||
position:fixed;
|
|
||||||
left:0px;
|
|
||||||
right:0px;
|
|
||||||
bottom:0px;
|
|
||||||
z-index: -15;
|
|
||||||
}
|
|
||||||
#copyrights .inset {
|
|
||||||
margin:0 auto;
|
|
||||||
padding:20px 50px 0;
|
|
||||||
position:relative;
|
|
||||||
}
|
|
||||||
#copyrights .logo {
|
|
||||||
width:164px;
|
|
||||||
height:40px;
|
|
||||||
background:url(../images/logo_footer.png);
|
|
||||||
text-indent:-999em;
|
|
||||||
}
|
|
||||||
#copyrights p {
|
|
||||||
font-size:12px;
|
|
||||||
color:#a0a0a0;
|
|
||||||
line-height:24px;
|
|
||||||
}
|
|
||||||
#copyrights .fl {
|
|
||||||
float:left;
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
label.switch {
|
|
||||||
margin: 1.5em auto;
|
|
||||||
}
|
|
||||||
input#switch {
|
|
||||||
position: absolute;
|
|
||||||
left: -9999px;
|
|
||||||
}
|
|
||||||
.switch {
|
|
||||||
position: relative;
|
|
||||||
display: block;
|
|
||||||
width: 5.5em;
|
|
||||||
height: 3em;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 1.5em;
|
|
||||||
-webkit-transition: 350ms;
|
|
||||||
transition: 350ms;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.07)), to(rgba(255, 255, 255, 0))), #dddddd;
|
|
||||||
background: -webkit-linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0)), #dddddd;
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0)), #dddddd;
|
|
||||||
-webkit-box-shadow: 0 0.07em 0.1em -0.1em rgba(0, 0, 0, 0.4) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7);
|
|
||||||
box-shadow: 0 0.07em 0.1em -0.1em rgba(0, 0, 0, 0.4) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7);
|
|
||||||
}
|
|
||||||
.switch::after {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
width: 2em;
|
|
||||||
height: 2em;
|
|
||||||
top: 0.5em;
|
|
||||||
left: 0.5em;
|
|
||||||
border-radius: 50%;
|
|
||||||
-webkit-transition: 250ms ease-in-out;
|
|
||||||
transition: 250ms ease-in-out;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#eeeeee));
|
|
||||||
background: -webkit-linear-gradient(#f5f5f5 10%, #eeeeee);
|
|
||||||
background: linear-gradient(#f5f5f5 10%, #eeeeee);
|
|
||||||
-webkit-box-shadow: 0 0.1em 0.15em -0.05em rgba(255, 255, 255, 0.9) inset, 0 0.2em 0.2em -0.12em rgba(0, 0, 0, 0.5);
|
|
||||||
box-shadow: 0 0.1em 0.15em -0.05em rgba(255, 255, 255, 0.9) inset, 0 0.2em 0.2em -0.12em rgba(0, 0, 0, 0.5);
|
|
||||||
}
|
|
||||||
.switch::before {
|
|
||||||
position: absolute;
|
|
||||||
content: '';
|
|
||||||
width: 4em;
|
|
||||||
height: 1.5em;
|
|
||||||
top: 0.75em;
|
|
||||||
left: 0.75em;
|
|
||||||
border-radius: 0.75em;
|
|
||||||
-webkit-transition: 250ms ease-in-out;
|
|
||||||
transition: 250ms ease-in-out;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.07)), to(rgba(255, 255, 255, 0.1))), #d0d0d0;
|
|
||||||
background: -webkit-linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0.1)), #d0d0d0;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.07)), to(rgba(255, 255, 255, 0.1))), #ec2727;
|
|
||||||
background: -webkit-linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0.1)), #ec2727;
|
|
||||||
background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.07)), to(rgba(255, 255, 255, 0.1))), #D95C5C;
|
|
||||||
background: -webkit-linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0.1)), #D95C5C;
|
|
||||||
background: linear-gradient(rgba(0, 0, 0, 0.07), rgba(255, 255, 255, 0.1)), #D95C5C;
|
|
||||||
-webkit-box-shadow: 0 0.08em 0.15em -0.1em rgba(0, 0, 0, 0.5) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7), 0 0 0 0 rgba(68, 204, 102, 0.7) inset;
|
|
||||||
box-shadow: 0 0.08em 0.15em -0.1em rgba(0, 0, 0, 0.5) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7), 0 0 0 0 rgba(68, 204, 102, 0.7) inset;
|
|
||||||
}
|
|
||||||
input:checked + .switch::before {
|
|
||||||
-webkit-box-shadow: 0 0.08em 0.15em -0.1em rgba(0, 0, 0, 0.5) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7), 3em 0 0 0 rgba(68, 204, 102, 0.7) inset;
|
|
||||||
box-shadow: 0 0.08em 0.15em -0.1em rgba(0, 0, 0, 0.5) inset, 0 0.05em 0.08em -0.01em rgba(255, 255, 255, 0.7), 3em 0 0 0 rgba(68, 204, 102, 0.7) inset;
|
|
||||||
}
|
|
||||||
input:checked + .switch::after {
|
|
||||||
left: 3em;
|
|
||||||
}
|
|
||||||
@@ -1,450 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata>
|
|
||||||
Created by FontForge 20100429 at Thu Sep 20 22:09:47 2012
|
|
||||||
By root
|
|
||||||
Copyright (C) 2012 by original authors @ fontello.com
|
|
||||||
</metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="icons" horiz-adv-x="947" >
|
|
||||||
<font-face
|
|
||||||
font-family="icons"
|
|
||||||
font-weight="500"
|
|
||||||
font-stretch="normal"
|
|
||||||
units-per-em="1000"
|
|
||||||
panose-1="2 0 6 3 0 0 0 0 0 0"
|
|
||||||
ascent="800"
|
|
||||||
descent="-200"
|
|
||||||
bbox="-0.666667 -200.023 1350 801.8"
|
|
||||||
underline-thickness="50"
|
|
||||||
underline-position="-100"
|
|
||||||
unicode-range="U+002B-1F6AB"
|
|
||||||
/>
|
|
||||||
<missing-glyph horiz-adv-x="364"
|
|
||||||
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
|
||||||
<glyph glyph-name=".notdef" horiz-adv-x="364"
|
|
||||||
d="M33 0v666h265v-666h-265zM66 33h199v600h-199v-600z" />
|
|
||||||
<glyph glyph-name=".null" horiz-adv-x="0"
|
|
||||||
/>
|
|
||||||
<glyph glyph-name="nonmarkingreturn" horiz-adv-x="333"
|
|
||||||
/>
|
|
||||||
<glyph glyph-name="plus" unicode="+" horiz-adv-x="610"
|
|
||||||
d="M565 350q19 0 24.5 -12.5t5.5 -37.5q0 -17 -1.5 -26t-8.5 -16.5t-20 -7.5h-210v-210q0 -19 -12.5 -24.5t-37.5 -5.5q-17 0 -26 1.5t-16.5 8.5t-7.5 20v210h-210q-19 0 -24.5 12.5t-5.5 37.5q0 17 1.5 26t8.5 16.5t20 7.5h210v210q0 19 12.5 24.5t37.5 5.5q17 0 26 -1.5
|
|
||||||
t16.5 -8.5t7.5 -20v-210h210z" />
|
|
||||||
<glyph glyph-name="hyphen" unicode="-" horiz-adv-x="610"
|
|
||||||
d="M565 350q19 0 24.5 -12.5t5.5 -37.5q0 -17 -1.5 -26t-8.5 -16.5t-20 -7.5h-520q-19 0 -24.5 12.5t-5.5 37.5q0 17 1.5 26t8.5 16.5t20 7.5h520z" />
|
|
||||||
<glyph glyph-name="at" unicode="@" horiz-adv-x="923"
|
|
||||||
d="M849 516q25 -64 25 -138q0 -103 -46 -182q-59 -92 -163 -92q-43 0 -70 21q-21 15 -30 36q-43 -55 -114 -55q-81 0 -122 58q-41 52 -41 141q0 91 48 147q53 64 142 64q52 0 90 -37l7 26h85l-18 -259q-2 -32 9 -44q7 -7 20 -7q35 0 60 34.5t34 73.5t9 75q0 108 -68 176
|
|
||||||
q-75 76 -208 76q-128 0 -214 -76q-102 -92 -102 -245q0 -133 86 -221q81 -82 204 -82q137 0 242 46l15 8v-96l-7 -3q-101 -50 -251 -50q-165 0 -269 97q-123 112 -123 308q0 182 116 294q57 56 133 84q77 31 168 31q178 0 284 -106q46 -44 69 -103zM465 199q46 0 69 40
|
|
||||||
q17 31 17 85q0 45 -16 74q-20 28 -60 28q-44 0 -64 -38q-19 -33 -19 -83q0 -23 4 -43.5t21.5 -41.5t47.5 -21z" />
|
|
||||||
<glyph glyph-name="h" unicode="h" horiz-adv-x="1228"
|
|
||||||
d="M281 54q0 56 32 138q56 164 76 215q31 82 51 144h-25h-31h-26q-51 10 -51 51q0 51 51 51h564q51 0 51 -51q0 -41 -36 -47q-15 -5 -31 -4h-15h-20h-32q11 -41 52 -144q61 -164 77 -210q30 -92 30 -143q0 -46 -40 -87q-31 -31 -87 -31h-461q-51 0 -93 31q-36 46 -36 87z
|
|
||||||
M481 345h318l-67 206h-184z" />
|
|
||||||
<glyph glyph-name="uni2139" unicode="ℹ" horiz-adv-x="490"
|
|
||||||
d="M367 800q48 0 74 -28t26 -70q0 -50 -39 -88t-95 -38q-47 0 -73.5 27t-25.5 73q0 45 36 84.5t97 39.5zM160 -200q-29 0 -45.5 13.5t-22.5 52.5t14 110l60 255q15 57 0 57q-13 0 -54.5 -18t-70.5 -38l-26 44q91 77 190 125t150 48q26 0 38.5 -20t11 -55.5t-14.5 -85.5
|
|
||||||
l-69 -268q-16 -63 5 -63q15 0 49.5 16t69.5 44l30 -41q-84 -86 -175 -131t-140 -45z" />
|
|
||||||
<glyph glyph-name="arrowleft" unicode="←" horiz-adv-x="789"
|
|
||||||
d="M0 329q0 20 15 35l345 346q15 15 35 15t34 -15l52 -52q15 -14 15 -34t-15 -35l-174 -174h433q21 0 35 -14t14 -35v-74q0 -20 -14 -34t-34 -14h-434l174 -174q15 -15 15 -35t-15 -34l-52 -52q-14 -15 -34 -15t-35 15l-345 345q-15 15 -15 35z" />
|
|
||||||
<glyph glyph-name="arrowup" unicode="↑" horiz-adv-x="789"
|
|
||||||
d="M-0.5 329q-0.5 20 14.5 35l346 345q15 15 35 15t34 -15l346 -345q14 -15 14 -35t-14 -35l-52 -51q-15 -15 -34.5 -15t-33.5 15l-175 173v-433q0 -20 -14 -34.5t-34 -14.5h-74q-21 0 -35 14.5t-14 34.5v433l-173 -173q-15 -15 -35 -15t-35 15l-51 51q-15 15 -15.5 35z" />
|
|
||||||
<glyph glyph-name="arrowright" unicode="→" horiz-adv-x="789"
|
|
||||||
d="M0 293v74q0 20 14 34t34 14h434l-174 174q-14 15 -14 35t14 34l52 52q15 15 35 15t34 -15l346 -346q14 -15 14 -34.5t-14 -34.5l-346 -346q-14 -15 -34 -15t-35 15l-52 52q-14 14 -14 34t14 35l174 174h-434q-20 0 -34 14t-14 35z" />
|
|
||||||
<glyph glyph-name="arrowdown" unicode="↓" horiz-adv-x="789"
|
|
||||||
d="M0 328q0 20 15 35l51 51q15 15 35 15t35 -15l173 -173v433q0 20 14.5 34.5t34.5 14.5h74q21 0 34.5 -14.5t13.5 -34.5v-433l175 173q14 15 33.5 15t34.5 -15l53 -51q15 -15 15 -35t-15 -35l-347 -345q-14 -15 -34 -15t-35 15l-345 345q-15 15 -15 35z" />
|
|
||||||
<glyph glyph-name="house" unicode="⌂" horiz-adv-x="930"
|
|
||||||
d="M903 285q16 -16 11 -27.5t-28 -11.5h-83v-308q0 -14 -1.5 -21t-8.5 -13.5t-22 -6.5h-204v310h-204v-310h-195q-19 0 -28.5 6.5t-11 13.5t-1.5 21v308h-83q-23 0 -28 11.5t11 27.5l401 401q16 16 37 16t37 -16z" />
|
|
||||||
<glyph glyph-name="uni25B4" unicode="▴" horiz-adv-x="490"
|
|
||||||
d="M15 100l230 400l230 -400h-460z" />
|
|
||||||
<glyph glyph-name="uni25B8" unicode="▸" horiz-adv-x="430"
|
|
||||||
d="M15 530l400 -230l-400 -230v460z" />
|
|
||||||
<glyph glyph-name="uni25BE" unicode="▾" horiz-adv-x="490"
|
|
||||||
d="M475 500l-230 -400l-230 400h460z" />
|
|
||||||
<glyph glyph-name="uni25C2" unicode="◂" horiz-adv-x="430"
|
|
||||||
d="M415 530v-460l-400 230z" />
|
|
||||||
<glyph glyph-name="uni2601" unicode="☁" horiz-adv-x="1291"
|
|
||||||
d="M1292 155q0 -100 -68 -173.5t-167 -80.5v-1h-783v1q-7 -1 -19 -1q-106 0 -180.5 74.5t-74.5 180.5q0 66 33.5 124.5t90.5 92.5q-7 29 -7 56q0 106 74.5 180.5t180.5 74.5q92 0 167 -63q39 82 116 131t167 49q129 0 221 -92t92 -221q0 -50 -15 -93q77 -26 124.5 -92.5
|
|
||||||
t47.5 -146.5z" />
|
|
||||||
<glyph glyph-name="uni2611" unicode="☑"
|
|
||||||
d="M0 92v474q0 32 12.5 61t33.5 50t50 34t62 13h553q13 0 26 -3l-96 -96h-483q-24 0 -41.5 -17.5t-17.5 -41.5v-474q0 -24 17.5 -41.5t41.5 -17.5h553q24 0 41 17.5t17 41.5v167l99 99v-266q0 -33 -12.5 -61t-34 -50t-50 -34.5t-60.5 -12.5h-553q-33 0 -62 12.5t-50 34.5
|
|
||||||
t-33.5 50t-12.5 61zM198 431q0 17 11 28l51 51q12 12 28.5 12t28.5 -12l175 -175l335 337q12 12 29.5 12t28.5 -12l51 -50q11 -12 11 -29t-11 -28l-365 -366l-51 -50q-12 -12 -28.5 -12t-28.5 12l-51 50l-203 204q-11 11 -11 28z" />
|
|
||||||
<glyph glyph-name="uni2661" unicode="♡" horiz-adv-x="880"
|
|
||||||
d="M795 591q70 -65 70 -156t-70 -156l-355 -330l-355 330q-70 65 -70 156t70 156q62 58 149.5 58t149.5 -58l56 -52l56 52q62 58 149.5 58t149.5 -58zM743 330q42 38 42 105t-37 100q-42 39 -102 39q-49 0 -102 -49l-104 -91l-104 91q-53 49 -102 49q-60 0 -102 -39
|
|
||||||
q-37 -33 -37 -100t42 -105l303 -286z" />
|
|
||||||
<glyph glyph-name="heart" unicode="♥" horiz-adv-x="890"
|
|
||||||
d="M804 591q70 -65 70 -156t-70 -156l-359 -330l-359 330q-70 65 -70 156t70 156q63 58 151 58t151 -58l57 -52l57 52q63 58 151 58t151 -58z" />
|
|
||||||
<glyph glyph-name="uni2691" unicode="⚑"
|
|
||||||
d="M0 645q0 32 23 55.5t56 23.5t56 -23.5t23 -55.5q0 -21 -10.5 -38.5t-28.5 -29.5v-623q0 -8 -6 -14t-14 -6h-40q-8 0 -13.5 6t-5.5 14v623q-18 12 -29 29.5t-11 38.5zM158 148v383q0 17 10 34t25 25q53 28 96.5 44t73.5 23q36 9 64 10q35 0 64 -6t55.5 -15.5t51.5 -21.5
|
|
||||||
l50 -27q33 -14 75 -16q36 -3 84.5 7t106.5 45q14 9 23.5 3.5t9.5 -22.5v-384q0 -16 -9.5 -33.5t-23.5 -25.5q-58 -35 -106.5 -45t-84.5 -8q-42 3 -75 17l-50 27q-25 12 -51.5 21.5t-55.5 15.5t-64 6q-28 -1 -64 -10q-30 -7 -73.5 -23t-96.5 -45q-15 -9 -25 -2.5t-10 23.5z
|
|
||||||
" />
|
|
||||||
<glyph glyph-name="uni2699" unicode="⚙" horiz-adv-x="787"
|
|
||||||
d="M0 271v117q0 7 7 9q20 6 42 9t43 5q4 0 8 0.5t9 1.5l11 29q6 14 14 29l-28 39q-15 20 -32 39q-6 5 0 13q20 24 42 46t46 42q5 5 13 0q11 -12 23.5 -21.5t24.5 -17.5q8 -5 15 -11t15 -11q27 15 58 24q3 30 6 54.5t8 47.5q2 9 10 9h117q9 0 9 -9q4 -20 7 -40l6 -41l3 -21
|
|
||||||
q15 -5 29 -10.5t28 -13.5l14 10l12 10l28 19q13 10 26 22q6 6 12 -1l12 -12q5 -5 11 -10l32 -31l31 -34q4 -7 0 -13q-14 -16 -28 -35l-31 -42q15 -30 25 -61q13 -3 26 -4.5t27 -3.5q11 -2 24 -4l25 -4q7 -2 7 -9v-117q0 -7 -7 -10q-19 -5 -40 -7.5t-42 -4.5q-5 -1 -10 -1.5
|
|
||||||
t-10 -1.5q-5 -15 -11 -29l-13 -29q12 -18 27.5 -38.5t32.5 -39.5q5 -5 0 -13q-40 -49 -89 -88q-5 -5 -12 0q-12 11 -24 20.5t-25 18.5q-7 5 -14.5 11t-14.5 11q-28 -15 -58 -24q-2 -25 -6 -51t-10 -51q-2 -9 -9 -9h-117q-8 0 -10 9q-3 20 -5.5 40t-5.5 41l-3 21l-29 10
|
|
||||||
q-15 5 -28 14l-14 -10l-13 -10q-27 -19 -53 -41q-7 -6 -13 1l-11 11l-12 11l-32 31q-15 16 -30 34q-5 7 0 13q16 19 30 39l29 38q-16 30 -26 61q-12 3 -25 4.5t-26 3.5t-26 3.5t-25 4.5q-7 2 -7 9zM275 329q0 -25 9.5 -46.5t25.5 -37.5t37.5 -25.5t46 -9.5t46 9.5t37.5 25.5
|
|
||||||
t25.5 37.5t9.5 46.5q0 24 -9.5 45.5t-25.5 37.5t-37.5 25.5t-46 9.5t-46 -9.5t-37.5 -25.5t-25.5 -37.5t-9.5 -45.5z" />
|
|
||||||
<glyph glyph-name="uni26A0" unicode="⚠" horiz-adv-x="894"
|
|
||||||
d="M5.5 -41q-14.5 25 6.5 59l387 671q19 35 48 35q28 0 49 -35l387 -671q20 -34 6 -59t-54 -25h-775q-40 0 -54.5 25zM168 53h558l-279 483zM389 391q0 6 4.5 10.5t9.5 4.5h89q5 0 9 -4.5t4 -10.5l-7 -192q0 -12 -14 -12h-73q-14 0 -14 12zM392 133q0 14 13 14h82
|
|
||||||
q14 0 14 -14l1 -51q0 -14 -14 -14h-82q-13 0 -13 14z" />
|
|
||||||
<glyph glyph-name="uni26A1" unicode="⚡" horiz-adv-x="430"
|
|
||||||
d="M55 -150q-4 3 35 92l79 182q40 93 38 99t-94.5 45t-97.5 54q-4 12 84 120.5t179.5 210t96.5 97.5t-74.5 -186t-77.5 -188q1 -3 95 -42.5t97 -56.5q3 -20 -174 -226t-186 -201z" />
|
|
||||||
<glyph glyph-name="uni26EF" unicode="⛯"
|
|
||||||
d="M0 346v88q0 7 5 7q15 4 31 6l32 4q5 0 8 0.5t7 0.5q6 22 18 44q-9 15 -21 30l-24 29q-5 6 0 10q15 18 31.5 35t34.5 32q6 4 10 -1q8 -8 18 -15l18 -14l23 -16q22 11 44 18q2 22 4.5 41t6.5 37q0 5 7 5h88q7 0 7 -6q3 -15 5.5 -30.5t4.5 -31.5l2 -15q21 -7 43 -18l20 15
|
|
||||||
l21 15q10 8 19 16q6 4 10 -1q5 -4 9 -8l9 -9l23 -23l24 -26q3 -6 0 -10q-11 -11 -22 -25l-23 -33q6 -11 11 -23l9 -23q8 -2 18 -3t21 -3l19 -2q9 -1 17 -4q7 -2 7 -7v-88q0 -6 -6 -8q-14 -3 -31 -5l-32 -4q-4 0 -14 -2q-7 -21 -18 -43q9 -15 20.5 -30t24.5 -29q4 -6 0 -10
|
|
||||||
q-15 -18 -31.5 -35t-34.5 -32q-7 -4 -10 1q-8 7 -17.5 14.5t-19.5 13.5q-5 6 -11 9.5t-11 7.5q-22 -11 -44 -18q-2 -18 -4 -38.5t-8 -39.5q-2 -5 -7 -5h-88q-7 0 -7 5q-3 15 -5 31l-4 31l-2 16q-21 7 -43 18l-10 -7q-5 -3 -10 -8l-21 -15q-10 -8 -19 -16q-7 -4 -10 1
|
|
||||||
q-5 4 -8 8l-10 9l-24 22q-12 12 -23 27q-4 4 0 9q12 14 23.5 29.5t20.5 29.5q-10 21 -19 46q-8 2 -18 3t-21 3l-19 2q-9 1 -19 3q-5 3 -5 8zM207 389q0 -37 26.5 -63.5t63.5 -26.5t63 26.5t26 63.5t-26 63t-63 26t-63.5 -26t-26.5 -63zM552 132q-3 6 4 9l22 8q10 4 22 8
|
|
||||||
q1 6 1.5 10t2.5 9.5t3.5 9t4.5 8.5q-8 11 -14 21t-13 20q-3 6 2 9l66 59q4 4 9 1l18 -15l19 -16q19 8 37 9q5 11 11 21.5t11 20.5q3 5 8 3l85 -26q5 -3 5 -9q-2 -11 -4.5 -22.5t-4.5 -22.5q9 -6 15 -13.5t12 -16.5q12 1 23.5 1.5t23.5 0.5q5 0 7 -5l19 -87q2 -5 -4 -8
|
|
||||||
q-11 -5 -21 -9l-23 -7q-1 -6 -1.5 -10t-2.5 -9.5t-4 -9t-4 -7.5l15 -21l12 -20q2 -5 -2 -9l-66 -60q-4 -4 -9 0q-9 8 -18.5 15t-17.5 15q-21 -8 -39 -9l-10 -22l-11 -20q-3 -5 -8 -3l-85 26q-5 3 -5 9q2 11 3.5 23t4.5 23q-15 13 -26 29q-12 -2 -24.5 -3t-23.5 1q-5 0 -8 5z
|
|
||||||
M589 573q0 5 6 8q10 2 20.5 4.5t21.5 4.5q2 4 3 8.5t3 8.5t5 7t5 8q-6 10 -10 20l-8 20q-2 4 2 8l67 44q5 3 9 -1q8 -7 15 -15l14 -16q17 3 35 3q12 19 25 34q3 4 9 3l72 -36q5 -3 3 -9q-2 -10 -5.5 -19.5t-6.5 -20.5q10 -13 19 -30q11 -1 22 -1.5t22 -2.5q5 -2 5 -7l5 -81
|
|
||||||
q0 -4 -5 -6q-10 -2 -20 -5l-22 -5q-2 -4 -3 -7.5t-3 -7.5q-3 -8 -9 -15q6 -11 10 -21t8 -19q2 -5 -3 -8l-66 -45q-6 -3 -9 1q-13 13 -29 32q-9 -2 -18 -3t-18 0q-6 -10 -12 -19l-13 -17q-3 -3 -9 -1l-72 36q-6 2 -3 7l6 21q3 10 7 20q-6 6 -11 13.5t-9 16.5q-11 1 -22 1.5
|
|
||||||
t-22 2.5q-5 0 -5 6zM693 148q-8 -23 3 -44.5t36 -29.5q23 -8 44.5 3t28.5 35q9 23 -2 44.5t-36 30.5q-23 7 -44.5 -4t-29.5 -35zM716.5 528q7.5 -21 27.5 -32q21 -9 42 -2t31 27q10 21 3 42t-27 30q-20 11 -41 3.5t-32 -27.5t-3.5 -41z" />
|
|
||||||
<glyph glyph-name="uni2708" unicode="✈" horiz-adv-x="1030"
|
|
||||||
d="M282 -170l125 400h-179l-113 -100h-100l80 170l-80 170h100l113 -100h179l-125 400h100l225 -400h258q6 0 16 -0.5t36 -4t46 -10.5t36 -21t16 -34q0 -31 -37.5 -48.5t-75.5 -19.5l-37 -2h-258l-225 -400h-100z" />
|
|
||||||
<glyph glyph-name="uni2709" unicode="✉" horiz-adv-x="930"
|
|
||||||
d="M45 536q-23 12 -28 33q1 19 25 21h846q33 0 23 -25q-7 -19 -26 -29l-375 -202q-19 -10 -45 -10t-45 10zM896 436q15 4 17 1.5t2 -12.5v-367q0 -16 -17 -32t-33 -16h-800q-16 0 -33 16t-17 32v367q0 10 2 12.5t17 -1.5l386 -202q19 -10 45 -10t45 10z" />
|
|
||||||
<glyph glyph-name="uni270D" unicode="✍"
|
|
||||||
d="M0 92v474q0 32 12.5 61t33.5 50t50 34t62 13h553q2 0 5 -0.5t5 -0.5l-98 -98h-465q-24 0 -41.5 -17.5t-17.5 -41.5v-474q0 -24 17.5 -41.5t41.5 -17.5h553q24 0 41 17.5t17 41.5v229l99 98v-327q0 -33 -12.5 -61t-34 -50t-50 -34.5t-60.5 -12.5h-553q-33 0 -62 12.5
|
|
||||||
t-50 34.5t-33.5 50t-12.5 61zM324 101l56 169l335 335l113 -114l-334 -335zM445 257q3 -4 8.5 -4t8.5 4l263 262q10 10 0.5 18.5t-18.5 -0.5l-262 -262q-9 -9 0 -18zM772 662l47 48q15 15 35 15t33 -15l24 -23l23 -23q13 -15 13.5 -34.5t-13.5 -34.5l-49 -47z" />
|
|
||||||
<glyph glyph-name="uni2715" unicode="✕" horiz-adv-x="500"
|
|
||||||
d="M467 142q17 -17 17 -42t-17 -42t-42 -17t-42 17l-133 151l-133 -151q-17 -17 -42 -17t-42 17t-17 42t17 42l137 158l-137 158q-17 17 -17 42t17 42t42 17t42 -17l133 -151l133 151q17 17 42 17t42 -17t17 -42t-17 -42l-137 -158z" />
|
|
||||||
<glyph glyph-name="uni2716" unicode="✖" horiz-adv-x="870"
|
|
||||||
d="M435 720q174 0 297 -123t123 -297t-123 -297t-297 -123t-297 123t-123 297t123 297t297 123zM521 300l153 153l-86 86l-153 -153l-153 153l-86 -86l153 -153l-153 -153l86 -87l153 154l153 -153l86 86z" />
|
|
||||||
<glyph glyph-name="uni2731" unicode="✱" horiz-adv-x="733"
|
|
||||||
d="M1 457q-5 19 5 37l37 64q11 18 30.5 23t37.5 -5l170 -99v197q0 21 14 35.5t36 14.5h72q21 0 35.5 -14.5t14.5 -34.5v-198l170 99q18 10 37.5 5t30.5 -23l35 -64q11 -18 6 -37t-23 -29l-171 -99l171 -99q18 -11 23 -29.5t-5 -36.5l-36 -65q-11 -17 -30.5 -22t-37.5 5
|
|
||||||
l-170 99v-197q0 -21 -14.5 -35.5t-35.5 -14.5h-72q-22 0 -36 14.5t-14 34.5v198l-170 -99q-18 -11 -37.5 -5.5t-30.5 22.5l-37 65q-9 18 -4.5 36.5t22.5 29.5l172 99l-172 99q-18 10 -23 29z" />
|
|
||||||
<glyph glyph-name="uni2753" unicode="❓" horiz-adv-x="610"
|
|
||||||
d="M15 476q5 130 77 202t203 72q128 0 214 -62t86 -183q0 -68 -42 -125q-13 -19 -88 -78l-46 -32q-29 -22 -42 -43.5t-14 -60.5q0 -13 -15 -13h-129q-16 0 -16 12q2 52 6.5 79.5t20.5 45.5q17 20 49.5 46t56.5 42l23 16q61 46 61 102q0 44 -25 79q-25 36 -93 36
|
|
||||||
q-67 0 -94 -44q-28 -45 -28 -91h-165zM292 54q45 -2 73.5 -31t27.5 -75t-31.5 -73t-75.5 -25q-44 2 -72.5 30.5t-27.5 73.5q1 46 31.5 74t74.5 26z" />
|
|
||||||
<glyph glyph-name="uni2757" unicode="❗" horiz-adv-x="789"
|
|
||||||
d="M0 329q0 82 31 153.5t84.5 125.5t125.5 85t154 31t153.5 -31t125 -85t84.5 -125.5t31 -153.5t-31 -153.5t-84.5 -125.5t-125 -85t-153.5 -31t-154 31t-125.5 85t-84.5 125.5t-31 153.5zM333 579l6 -365q2 -15 16 -15h80q14 0 14 15l8 365q1 6 -4 11q-4 4 -11 4h-95
|
|
||||||
q-7 0 -10 -4q-4 -4 -4 -11zM336 66q0 -15 15 -15h89q5 0 10 4t5 11v86q0 6 -5 10.5t-10 4.5h-89q-15 0 -15 -15v-86z" />
|
|
||||||
<glyph glyph-name="uni2795" unicode="➕" horiz-adv-x="870"
|
|
||||||
d="M435 720q174 0 297 -123t123 -297t-123 -297t-297 -123t-297 123t-123 297t123 297t297 123zM486 249h202l-1 102h-201v201h-102v-201h-202v-102h202v-202h102v202z" />
|
|
||||||
<glyph glyph-name="uni27A2" unicode="➢" horiz-adv-x="890"
|
|
||||||
d="M863 717q6 -6 9 -12t2.5 -14.5t-2 -15.5t-7.5 -19.5t-12 -23.5l-16 -30l-18 -37q-54 -113 -147 -287l-158 -292l-65 -117l-54 380l-380 54q442 248 696 370l37 18l30 16q11 6 23.5 12t19.5 7.5t15.5 2t14.5 -2.5t12 -9zM772 619l-304 -279l28 -233z" />
|
|
||||||
<glyph glyph-name="uni27A6" unicode="➦"
|
|
||||||
d="M0 4q0 107 37 202t112 166t190.5 112.5t273.5 42.5v150q0 37 18 45t46 -18l252 -254q18 -17 18 -42q0 -24 -18 -42l-252 -254q-26 -26 -45 -18t-19 44v170q-135 -1 -237 -29t-172.5 -76t-110.5 -112.5t-51 -139.5q-2 -17 -19 -17h-1q-17 0 -19 17q-3 26 -3 53z" />
|
|
||||||
<glyph glyph-name="uni27F2" unicode="⟲" horiz-adv-x="970"
|
|
||||||
d="M546 710q169 0 289 -120t120 -290t-120 -290t-289 -120q-140 0 -251 87l70 75q83 -60 181 -60q127 0 217 90.5t90 217.5t-90 217t-217 90q-124 0 -213 -86.5t-93 -210.5h143l-184 -205l-184 205h123q4 167 123 283.5t285 116.5z" />
|
|
||||||
<glyph glyph-name="uni27F3" unicode="⟳" horiz-adv-x="970"
|
|
||||||
d="M424 710q166 0 285 -116.5t123 -283.5h123l-184 -205l-184 205h143q-4 124 -93 210.5t-213 86.5q-127 0 -217 -90t-90 -217t90 -217.5t217 -90.5q98 0 181 60l70 -75q-111 -87 -251 -87q-169 0 -289 120t-120 290t120 290t289 120z" />
|
|
||||||
<glyph glyph-name="uni2B0C" unicode="⬌" horiz-adv-x="394"
|
|
||||||
d="M0.5 131q4.5 12 28.5 12h100v372h-100q-24 0 -28.5 11t12.5 28l158 159q10 11 27 11q16 0 26 -11l158 -159q17 -16 12 -27.5t-29 -11.5h-100v-372h100q24 0 29 -11t-12 -28l-158 -159q-10 -11 -27 -11q-16 0 -26 11l-158 159q-17 15 -12.5 27z" />
|
|
||||||
<glyph glyph-name="uni2B0D" unicode="⬍" horiz-adv-x="789"
|
|
||||||
d="M0 310q0 16 11 26l158 159q16 16 27.5 11.5t11.5 -28.5v-101h373v101q0 24 11 28.5t28 -12.5l159 -158q10 -10 10 -26t-10 -26l-159 -159q-16 -17 -27.5 -12t-11.5 29v101h-373v-101q0 -24 -11 -28.5t-28 12.5l-158 158q-11 10 -11 26z" />
|
|
||||||
<glyph glyph-name="uni2ECF" unicode="⻏" horiz-adv-x="789"
|
|
||||||
d="M0 -30v102q0 15 10.5 25.5t25.5 10.5h718q14 0 24.5 -10.5t10.5 -25.5v-102q0 -15 -10.5 -25.5t-24.5 -10.5h-718q-15 0 -25.5 10.5t-10.5 25.5zM3 239q-9 23 8 39l358 359q11 11 25.5 11t25.5 -11l359 -359q17 -16 7 -39q-8 -22 -32 -22h-718q-24 0 -33 22z" />
|
|
||||||
<glyph glyph-name="uniE700" unicode="" horiz-adv-x="1030"
|
|
||||||
d="M634 78q89 -32 135 -65t46 -58v-105h-800v201q13 6 29.5 11.5t32 10t19.5 5.5q94 34 129.5 69t35.5 95q0 20 -10 31.5t-24 31.5t-21 58q-2 12 -9.5 17.5t-14.5 8t-14 17t-9 43.5q0 15 4.5 25.5t8.5 13.5l5 3q-9 50 -13 88q-2 21 5.5 47.5t27.5 55.5t63.5 48.5t104.5 19.5
|
|
||||||
t104.5 -19.5t63.5 -48.5t27.5 -55.5t5.5 -47.5l-13 -88q18 -8 18 -42q-2 -29 -9 -43.5t-14 -17t-14.5 -8t-9.5 -17.5q-7 -38 -21 -58t-24 -31.5t-10 -31.5q0 -60 35.5 -95t129.5 -69zM865 350h150v-100h-150v-150h-100v150h-150v100h150v150h100v-150z" />
|
|
||||||
<glyph glyph-name="uniE701" unicode="" horiz-adv-x="413"
|
|
||||||
d="M0.5 423.5q4.5 13.5 26.5 17.5l251 35l111 228q10 20 25 20v-667l-223 -118q-22 -10 -33.5 -1.5t-6.5 31.5l43 248l-181 177q-17 16 -12.5 29.5z" />
|
|
||||||
<glyph glyph-name="uniE704" unicode="" horiz-adv-x="950"
|
|
||||||
d="M469 760q94 1 179.5 -34.5t148 -96t100 -145t38.5 -178.5t-34.5 -179.5t-96 -148t-145 -100t-178.5 -38.5t-179.5 34.5t-148 96t-100 145t-38.5 178.5t34.5 179.5t96 148t145 100t178.5 38.5zM535 84q1 31 -18 50t-50 20q-29 0 -48 -19t-20 -47q-1 -30 18 -48.5t50 -19.5
|
|
||||||
q29 0 48 18t20 46zM475 486q67 0 67 -66q0 -14 -11 -31.5t-24 -27.5q-3 -1 -16 -10.5t-33 -25.5t-31 -28q-16 -19 -18 -87h107q0 21 5 34q6 16 28 34l28 19q30 23 43.5 36t26 37.5t12.5 55.5q0 74 -53.5 114t-135.5 40q-84 0 -128 -46.5t-48 -130.5h111v5q0 28 16 53
|
|
||||||
q15 25 54 25z" />
|
|
||||||
<glyph glyph-name="uniE705" unicode="" horiz-adv-x="950"
|
|
||||||
d="M469 760q94 1 179.5 -34.5t148 -96t100 -145t38.5 -178.5t-34.5 -179.5t-96 -148t-145 -100t-178.5 -38.5t-179.5 34.5t-148 96t-100 145t-38.5 178.5t34.5 179.5t96 148t145 100t178.5 38.5zM520 607q-43 0 -65.5 -23.5t-22.5 -50.5q-1 -28 15.5 -43.5t48.5 -15.5
|
|
||||||
q38 0 61 21.5t23 52.5q0 59 -60 59zM400 12q30 0 84.5 27t105.5 78l-18 24q-46 -36 -72 -36q-12 0 -3 38l42 159q26 96 -21 96q-31 0 -91 -28.5t-115 -74.5l16 -26q17 11 42.5 22t33.5 11t0 -34l-37 -151q-27 -105 33 -105z" />
|
|
||||||
<glyph glyph-name="uniE708" unicode=""
|
|
||||||
d="M0 -26v276q0 17 11.5 28.5t28.5 11.5h355q17 0 28.5 -11.5t11.5 -28.5v-276q0 -17 -11.5 -28.5t-28.5 -11.5h-355q-17 0 -28.5 11.5t-11.5 28.5zM0 408v277q0 16 11.5 27.5t28.5 11.5h355q17 0 28.5 -11.5t11.5 -27.5v-277q0 -17 -11.5 -28t-28.5 -11h-355
|
|
||||||
q-17 0 -28.5 11t-11.5 28zM514 -26v276q0 17 11 28.5t28 11.5h355q16 0 27.5 -11.5t11.5 -28.5v-276q0 -17 -11.5 -28.5t-27.5 -11.5h-355q-17 0 -28 11.5t-11 28.5zM514 408v277q0 16 11 27.5t28 11.5h355q16 0 27.5 -11.5t11.5 -27.5v-277q0 -17 -11.5 -28t-27.5 -11h-355
|
|
||||||
q-17 0 -28 11t-11 28z" />
|
|
||||||
<glyph glyph-name="uniE70B" unicode=""
|
|
||||||
d="M0 328.5q0 22.5 13 42.5q40 64 91.5 115t111 86.5t124.5 54t134 18.5q27 0 54 -4t53 -9l45 81q5 8 13 10q6 3 15 -1l68 -39q7 -5 10 -12.5t-1 -15.5l-398 -710q-4 -8 -12 -10q-2 -1 -5 -1t-10 2l-69 39q-8 4 -10 12t2 16l34 59q-75 34 -138.5 91t-111.5 134
|
|
||||||
q-13 19 -13 41.5zM79 329q42 -67 98.5 -117.5t123.5 -81.5l30 54q-45 32 -71.5 81.5t-26.5 109.5q0 40 13 76.5t35 66.5q-60 -31 -111.5 -78.5t-90.5 -110.5zM316 375q0 -11 8.5 -20t21 -9t21 9t8.5 20q0 40 27 67.5t67 27.5q13 0 21.5 9t8.5 20q0 13 -8.5 21.5t-21.5 8.5
|
|
||||||
q-31 0 -59.5 -12t-49 -32.5t-32.5 -49t-12 -60.5zM477 13l45 82q107 13 196 74.5t150 159.5q-55 87 -134 145l39 71q47 -34 88 -77t75 -97q11 -20 11 -42.5t-11 -41.5q-82 -131 -201.5 -202t-257.5 -72zM556 155l146 262q2 -10 3 -20t1 -22q0 -38 -11 -72t-31 -62.5
|
|
||||||
t-48 -50.5t-60 -35z" />
|
|
||||||
<glyph glyph-name="uniE70D" unicode="" horiz-adv-x="945"
|
|
||||||
d="M0 434v229q0 25 18 43t43 18h228q26 0 57 -12.5t48 -30.5l338 -377q16 -18 16.5 -43.5t-16.5 -43.5l-266 -264q-18 -18 -43 -18.5t-43 18.5l-337 377q-17 19 -30 49t-13 55zM99 566q0 -24 17.5 -41.5t41.5 -17.5t41.5 17.5t17.5 41.5t-17.5 41.5t-41.5 17.5t-41.5 -17.5
|
|
||||||
t-17.5 -41.5zM399 723h87q26 0 57 -13t48 -31l337 -376q17 -19 17.5 -44t-17.5 -43l-265 -264q-18 -18 -43 -18.5t-43 18.5l-6 7l258 258q18 18 17.5 43.5t-17.5 43.5l-336 377q-16 16 -43 28t-51 14z" />
|
|
||||||
<glyph glyph-name="uniE711" unicode="" horiz-adv-x="1030"
|
|
||||||
d="M776 443q99 0 169 -68.5t70 -165.5t-70 -165.5t-169 -68.5h-191v190h105l-175 230l-175 -230h105v-190h-249q-75 0 -128 52t-53 125t53 125t128 52q7 0 21 -2q-3 18 -3 38q0 108 78 184t188 76q89 0 159.5 -52t95.5 -133q19 3 41 3z" />
|
|
||||||
<glyph glyph-name="uniE714" unicode="" horiz-adv-x="1350"
|
|
||||||
d="M277 350l312 -311l-139 -139l-450 450l450 450l139 -139zM900 800l450 -450l-450 -450l-139 139l312 311l-312 311z" />
|
|
||||||
<glyph glyph-name="uniE715" unicode=""
|
|
||||||
d="M0 92v474q0 32 12.5 61t33.5 50t50 34t62 13h429q-3 -16 -3 -33v-26q-84 -11 -159 -40h-267q-24 0 -41.5 -17.5t-17.5 -41.5v-474q0 -24 17.5 -41.5t41.5 -17.5h553q24 0 41 17.5t17 41.5v57q9 5 17 11t16 14l66 65v-147q0 -33 -12.5 -61t-34 -50t-50 -34.5t-60.5 -12.5
|
|
||||||
h-553q-33 0 -62 12.5t-50 34.5t-33.5 50t-12.5 61zM198 134v19q0 85 29 160.5t89 132t151 89t215 33.5v119q0 29 15 35.5t36 -14.5l201 -201q13 -13 13 -34q0 -20 -13 -32l-201 -202q-21 -21 -36 -14.5t-15 36.5v133q-106 0 -187 -22t-137 -60t-87.5 -89.5t-39.5 -110.5
|
|
||||||
q-3 -14 -16 -14t-15 14q-2 12 -2 22z" />
|
|
||||||
<glyph glyph-name="uniE716" unicode=""
|
|
||||||
d="M0 -46v178q0 24 9.5 45.5t26 37.5t38 25.5t45.5 9.5h710q25 0 46.5 -9.5t37 -25.5t25 -37.5t9.5 -45.5v-178q0 -20 -20 -20h-907q-20 0 -20 20zM138 33q0 -8 5.5 -13.5t14.5 -5.5h631q9 0 14.5 5.5t5.5 13.5v20q0 9 -5.5 14.5t-14.5 5.5h-631q-9 0 -14.5 -5.5t-5.5 -14.5
|
|
||||||
v-20zM158 309v376q0 16 11.5 27.5t28.5 11.5h335v-197q0 -25 17 -42.5t42 -17.5h197v-158h-631zM592 527v197l197 -197h-197z" />
|
|
||||||
<glyph glyph-name="uniE718" unicode="" horiz-adv-x="830"
|
|
||||||
d="M715 650q41 0 70.5 -29.5t29.5 -70.5v-350q0 -41 -29.5 -70.5t-70.5 -29.5h-200v-150l-200 150h-200q-41 0 -70.5 29.5t-29.5 70.5v350q0 41 29.5 70.5t70.5 29.5h600z" />
|
|
||||||
<glyph glyph-name="uniE720" unicode=""
|
|
||||||
d="M0 443q0 58 30.5 109t82.5 89t122 60.5t150 22.5t150 -22.5t122 -60.5t82 -89t30 -109q0 -59 -30 -110t-82 -89t-122 -60t-150 -22q-19 0 -36.5 1.5t-35.5 3.5q-36 -28 -77 -48t-89 -30q-21 -5 -42 -6q-12 -2 -17 11v1q-2 6 2 10l8 8q18 18 31.5 39t22.5 67
|
|
||||||
q-70 38 -111 96t-41 128zM321 77l13 8q13 -2 26 -2h25q98 0 182.5 27.5t147 76t98 114.5t35.5 142q0 20 -3 41q48 -38 75 -86t27 -104q0 -69 -41 -126.5t-110 -96.5q8 -46 22 -66.5t31 -38.5q5 -5 8.5 -9.5t1.5 -9.5q-1 -6 -6.5 -10t-10.5 -3q-11 2 -21 3.5t-21 4.5
|
|
||||||
q-93 20 -165 76q-18 -2 -36 -3.5t-37 -1.5q-69 0 -129.5 17t-111.5 47z" />
|
|
||||||
<glyph glyph-name="uniE722" unicode="" horiz-adv-x="1030"
|
|
||||||
d="M915 700q41 0 70.5 -29.5t29.5 -70.5v-600q0 -41 -29.5 -70.5t-70.5 -29.5h-800q-41 0 -70.5 29.5t-29.5 70.5v600q0 41 29.5 70.5t70.5 29.5h800zM915 0v600h-800v-600h800zM465 195v-90h-250v90h250zM465 345v-90h-250v90h250zM465 495v-90h-250v90h250zM810 175
|
|
||||||
q2 0 3.5 -17.5t1.5 -35.5v-17h-250q0 70 5 70q10 2 24 7t37.5 22t23.5 37q0 16 -13.5 34.5t-27.5 45.5t-14 64q0 55 20.5 82.5t69.5 27.5t69.5 -27.5t20.5 -82.5q0 -37 -14 -64t-27.5 -45.5t-13.5 -34.5q0 -20 21.5 -36.5t42.5 -22.5z" />
|
|
||||||
<glyph glyph-name="uniE724" unicode="" horiz-adv-x="557"
|
|
||||||
d="M0 445q0 58 22 108.5t60 88t88.5 60t108.5 22.5t108.5 -22.5t88.5 -60t60 -88t22 -108.5q0 -42 -12.5 -79t-31.5 -71l-189 -327q-19 -34 -46 -34t-46 34l-189 327q-19 34 -31.5 71.5t-12.5 78.5zM141 444.5q0 -28.5 10.5 -53.5t29.5 -44t44.5 -29.5t53.5 -10.5t53.5 10.5
|
|
||||||
t44 29.5t29.5 44t11 53.5t-11 54t-29.5 44t-44 29.5t-53.5 11t-53.5 -11t-44.5 -29.5t-29.5 -44t-10.5 -54z" />
|
|
||||||
<glyph glyph-name="uniE729" unicode="" horiz-adv-x="682"
|
|
||||||
d="M0 546.5q0 8.5 0.5 17t0.5 18.5q12 6 34.5 10t49.5 7.5t55 5.5t52 3q-3 17 -1.5 33.5t7.5 34.5q1 5 6 12.5t19.5 15.5t42.5 14t75 6t75 -6t42.5 -14t19.5 -16l6 -13q7 -18 8 -34t-1 -33q23 -1 51.5 -3t55.5 -5.5t49 -7.5t34 -10q1 -10 1 -18v-17v-17q0 -8 -1 -18
|
|
||||||
q-10 -6 -32 -10.5t-48 -7.5t-53.5 -5t-50.5 -3t-39 -1.5t-17 -0.5l-100 -1h-14h-29q-20 0 -57 1q-2 0 -17.5 0.5t-38.5 1.5t-50.5 3t-53.5 5t-47.5 7.5t-32.5 10.5q0 10 -0.5 18t-0.5 16.5zM71 444q43 -6 88.5 -8.5t79.5 -3.5q9 -1 24 -1h78h77q15 0 24 1q35 1 80.5 3.5
|
|
||||||
t89.5 8.5q-6 -80 -9 -163t-7 -163q-1 -20 -1.5 -44.5t-2 -47.5t-6.5 -42.5t-15 -30.5q-13 -11 -44.5 -15t-61.5 -4h-248q-31 0 -62 4t-43 15q-11 11 -15.5 30.5t-6.5 42.5t-2.5 47.5t-1.5 44.5q-4 80 -7.5 163t-7.5 163zM155 337q1 -15 1 -29t1 -23q0 -12 1 -20
|
|
||||||
q2 -36 3.5 -72t3.5 -70q1 -9 1 -18v-21v-13q0 -6 0.5 -14.5t1.5 -20.5q1 -8 10 -14.5t15 -6.5q5 -1 10 -1t9 -1h11q5 0 5 20v301q0 9 -5.5 14.5t-14.5 5.5l-34 2q-9 0 -14 -5t-5 -14zM268.5 625.5q0.5 -6.5 2.5 -15.5q9 1 21 1h49l71 -1q1 9 1.5 15.5t-0.5 11.5
|
|
||||||
q-12 4 -31.5 5.5t-40.5 1.5t-40.5 -1.5t-31.5 -5.5q-1 -5 -0.5 -11.5zM307 33q0 -8 5.5 -14t13.5 -6h30q8 0 14 6t6 14v300q0 9 -11.5 14t-15.5 5h-15q-3 0 -15 -5t-12 -14v-300zM455 33q0 -20 4 -20h12q3 1 13.5 1.5t15.5 0.5q5 1 9 7t5 14q0 12 0.5 20.5t0.5 14.5
|
|
||||||
q0 7 1 13v21q0 9 1 18q2 34 3 69.5t3 71.5q0 9 1 21q1 9 1.5 23t1.5 29q0 9 -5 14t-14 5l-34 -2q-9 0 -14 -5.5t-5 -14.5v-301z" />
|
|
||||||
<glyph glyph-name="uniE730" unicode="" horiz-adv-x="730"
|
|
||||||
d="M615 750q41 0 70.5 -29.5t29.5 -70.5v-700q0 -41 -29.5 -70.5t-70.5 -29.5h-500q-41 0 -70.5 29.5t-29.5 70.5v700q0 41 29.5 70.5t70.5 29.5h500zM615 -50v700h-500v-700h500z" />
|
|
||||||
<glyph glyph-name="uniE736" unicode="" horiz-adv-x="730"
|
|
||||||
d="M665 550q21 0 35.5 -14.5t14.5 -35.5v-600q0 -21 -14.5 -35.5t-35.5 -14.5h-400q-21 0 -35.5 14.5t-14.5 35.5v150h-150q-21 0 -35.5 14.5t-14.5 35.5v600q0 21 14.5 35.5t35.5 14.5h400q21 0 35.5 -14.5t14.5 -35.5v-150h150zM115 150h100v351q0 21 14 35t35 14h151v100
|
|
||||||
h-300v-500zM615 -50v500h-300v-500h300z" />
|
|
||||||
<glyph glyph-name="uniE73A" unicode="" horiz-adv-x="790"
|
|
||||||
d="M15 680q155 0 295.5 -60t242.5 -162t162 -242.5t60 -295.5h-118q0 130 -51 249t-137 205t-205 137t-249 51v118zM15 443q142 0 262.5 -70t190.5 -190.5t70 -262.5h-118q0 167 -119 286t-286 119v118zM128 147q47 0 80.5 -33.5t33.5 -80.5t-33.5 -80t-80.5 -33t-80 33
|
|
||||||
t-33 80t33 80.5t80 33.5z" />
|
|
||||||
<glyph glyph-name="uniE73D" unicode="" horiz-adv-x="1006"
|
|
||||||
d="M209 74q33 0 56 -23t23 -56t-23 -55t-56 -22q-31 0 -54.5 22t-23.5 55t22.5 56t55.5 23zM738.5 74q32.5 0 55 -23t22.5 -56t-22.5 -55t-55 -22t-55.5 22t-23 55t23 56t55.5 23zM261 662h565q26 0 40.5 -13.5t14.5 -36.5v-233q0 -20 -13 -34t-33 -16l-505 -57
|
|
||||||
q-20 -2 -29 -16.5t0 -28t31 -13.5h479q27 0 37.5 -21t0 -41t-37.5 -20h-489q-52 0 -84 33t-27 77t47 76l43 32l-155 339h-76q-28 0 -39 22.5t0 45t39 22.5h89q31 0 45 -12.5t26 -41.5zM606 592h-130l26 -84h124zM294 592l38 -84h127l-28 84h-137zM402 351l100 10l-32 101
|
|
||||||
h-119zM546 366l113 12l-25 84h-119zM812 395v67h-132l20 -78zM812 508v84h-161l19 -84h142z" />
|
|
||||||
<glyph glyph-name="uniE744" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 -22v269q0 27 13.5 33t33.5 -13l86 -85l141 141q6 6 15.5 6t16.5 -6l82 -82q7 -8 7 -17.5t-7 -15.5l-141 -141l86 -85q20 -20 13.5 -34t-33.5 -14h-268q-19 0 -31 13q-14 14 -14 31zM395 434.5q0 9.5 6 15.5l141 141l-85 85q-20 20 -14 34t34 14h267q19 0 32 -13
|
|
||||||
q13 -14 13 -32v-268q0 -27 -13.5 -33t-33.5 13l-85 85l-141 -140q-7 -7 -16 -7t-17 7l-82 81q-6 8 -6 17.5z" />
|
|
||||||
<glyph glyph-name="uniE746" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 38.5q0 9.5 6 16.5l141 140l-85 86q-20 20 -13.5 33.5t33.5 13.5h267q20 0 32 -13q14 -13 14 -31v-269q0 -27 -14 -33t-34 14l-85 86l-141 -141q-6 -8 -15.5 -8t-17.5 8l-82 82q-6 6 -6 15.5zM395 373v269q0 27 13.5 33t33.5 -14l85 -86l141 141q7 8 16.5 8t16.5 -8
|
|
||||||
l82 -82q6 -6 6 -15.5t-6 -16.5l-141 -140l85 -86q20 -20 14 -33.5t-34 -13.5h-267q-21 0 -32 13q-13 13 -13 31z" />
|
|
||||||
<glyph glyph-name="uniE750" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 394.5q0 68.5 26 128.5t70.5 104.5t104.5 70.5t128 26t128 -26t104.5 -70.5t70.5 -104.5t26 -129q0 -50 -14.5 -95.5t-40.5 -85.5l177 -176q9 -10 9 -24t-9 -23l-46 -46q-10 -10 -23.5 -10t-23.5 10l-176 177q-40 -27 -85.5 -41t-96.5 -14q-68 0 -128 25.5t-104.5 70.5
|
|
||||||
t-70.5 104.5t-26 128zM132 394q0 -41 15 -76.5t42 -62.5t63 -42.5t77.5 -15.5t77 15.5t62.5 42.5t42 62.5t15 76.5t-15 77t-42 63t-62.5 42.5t-77 15.5t-77.5 -15.5t-63 -42.5t-42 -63t-15 -77zM198 378v33q0 7 4.5 12t11.5 5h82v82q0 6 4.5 11t12.5 5h32q8 0 12.5 -5
|
|
||||||
t4.5 -11v-82h82q17 0 17 -17v-33q0 -16 -17 -16h-82v-83q0 -16 -17 -16h-32q-17 0 -17 16v83h-82q-7 0 -11.5 4.5t-4.5 11.5z" />
|
|
||||||
<glyph glyph-name="uniE751" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 394.5q0 68.5 26 128.5t70.5 104.5t104.5 70.5t128 26t128 -26t104.5 -70.5t70.5 -104.5t26 -129q0 -50 -14.5 -95.5t-40.5 -85.5l177 -176q9 -10 9 -24t-9 -23l-46 -46q-10 -10 -23.5 -10t-23.5 10l-176 177q-40 -27 -85.5 -41t-96.5 -14q-68 0 -128 25.5t-104.5 70.5
|
|
||||||
t-70.5 104.5t-26 128zM132 394q0 -41 15 -76.5t42 -62.5t63 -42.5t77.5 -15.5t77 15.5t62.5 42.5t42 62.5t15 76.5t-15 77t-42 63t-62.5 42.5t-77 15.5t-77.5 -15.5t-63 -42.5t-42 -63t-15 -77zM198 378v33q0 7 4.5 12t11.5 5h230q17 0 17 -17v-33q0 -16 -17 -16h-230
|
|
||||||
q-7 0 -11.5 4.5t-4.5 11.5z" />
|
|
||||||
<glyph glyph-name="uniE75C" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 406.5q0 19.5 15 34.5l51 52q15 15 35 15t35 -15l261 -262l257 262q14 15 34 15t35 -15l52 -52q14 -15 15 -34.5t-14 -34.5l-347 -346q-14 -15 -34 -15t-35 15l-345 346q-15 15 -15 34.5z" />
|
|
||||||
<glyph glyph-name="uniE75D" unicode="" horiz-adv-x="495"
|
|
||||||
d="M0 327.5q0 19.5 15 34.5l345 346q15 15 35 15t34 -15l52 -52q15 -14 15 -34t-15 -35l-261 -262l261 -257q15 -15 15 -34.5t-15 -34.5l-52 -52q-14 -15 -34 -15t-35 15l-345 346q-15 15 -15 34.5z" />
|
|
||||||
<glyph glyph-name="uniE75E" unicode="" horiz-adv-x="495"
|
|
||||||
d="M0 34q0 20 15 35l261 261l-261 258q-15 15 -15 34.5t15 33.5l51 53q15 15 35 15t35 -15l345 -346q15 -15 15 -35t-15 -35l-345 -345q-15 -15 -35 -15t-35 15l-51 51q-15 15 -15 35z" />
|
|
||||||
<glyph glyph-name="uniE75F" unicode="" horiz-adv-x="789"
|
|
||||||
d="M-0.5 116.5q-0.5 19.5 14.5 34.5l346 346q15 15 35 15t34 -15l346 -346q14 -15 14 -34.5t-14 -33.5l-52 -53q-15 -15 -35 -15t-34 15l-261 262l-257 -262q-15 -15 -35 -15t-35 15l-51 53q-15 14 -15.5 33.5z" />
|
|
||||||
<glyph glyph-name="uniE762" unicode=""
|
|
||||||
d="M0 -34v726q0 20 18 30q20 7 33 -8l335 -362q9 -9 9 -23t-9 -23l-335 -362q-9 -10 -22 -10q-3 0 -11 2q-18 9 -18 30zM395 -34v726q0 20 18 30q20 7 32 -8l336 -362q8 -9 8 -23t-8 -23l-336 -362q-8 -10 -21 -10q-3 0 -11 2q-18 9 -18 30zM789 -27q0 -16 12 -27.5
|
|
||||||
t27 -11.5h79q17 0 28.5 11.5t11.5 27.5v711q0 17 -11.5 28.5t-28.5 11.5h-79q-15 0 -27 -11.5t-12 -28.5v-711z" />
|
|
||||||
<glyph glyph-name="uniE763" unicode=""
|
|
||||||
d="M0 -27q0 -16 11.5 -27.5t27.5 -11.5h79q17 0 28.5 11.5t11.5 27.5v711q0 17 -11.5 28.5t-28.5 11.5h-79q-16 0 -27.5 -11.5t-11.5 -28.5v-711zM158 329q0 14 8 22l336 363q7 10 21 10q3 0 12 -2q18 -10 18 -31v-725q0 -21 -18 -30q-20 -7 -33 8l-336 362q-8 9 -8 23z
|
|
||||||
M553 329q0 14 8 22l336 363q7 10 21 10q3 0 11 -2q18 -10 18 -31v-725q0 -21 -18 -30q-20 -7 -32 8l-336 362q-8 9 -8 23z" />
|
|
||||||
<glyph glyph-name="uniE782" unicode="" horiz-adv-x="1228"
|
|
||||||
d="M256 38.5q0 41.5 30.5 72t71.5 30.5h513q40 0 71 -30.5t31 -72t-31 -72t-71 -30.5h-513q-41 0 -71.5 30.5t-30.5 72zM256 294.5q0 40.5 30.5 71.5t71.5 31h513q40 0 71 -31t31 -71.5t-31 -71.5t-71 -31h-513q-41 0 -71.5 31t-30.5 71.5zM256 550q0 41 30.5 72t71.5 31
|
|
||||||
h513q40 0 71 -31t31 -72t-31 -71.5t-71 -30.5h-513q-41 0 -71.5 30.5t-30.5 71.5z" />
|
|
||||||
<glyph glyph-name="uniE78E" unicode="" horiz-adv-x="750"
|
|
||||||
d="M0 309q0 94 43 175.5t121 134.5q7 4 15 4q7 -1 13 -9l44 -65q10 -17 -5 -27q-54 -37 -83 -93t-29 -120q0 -53 20 -99.5t54.5 -81.5t81.5 -55t100 -20t100 20t82 55t55 81.5t20 99.5q0 64 -29.5 120t-83.5 93q-6 3 -8 12q-2 7 3 15l45 65q5 7 12.5 8.5t14.5 -3.5
|
|
||||||
q77 -53 121 -134.5t44 -175.5q0 -78 -29.5 -146t-81 -119t-119.5 -80.5t-146 -29.5t-146 29.5t-119 80.5t-80.5 119t-29.5 146zM316 349v355q0 20 20 20h79q8 0 14 -6t6 -14v-355q0 -8 -6 -14t-14 -6h-79q-20 0 -20 20z" />
|
|
||||||
<glyph glyph-name="uniE792" unicode=""
|
|
||||||
d="M0 -66v790h79v-790h-79zM117 -66v790h19v-790h-19zM183 -66v790h60v-790h-60zM280 -66v790h40v-790h-40zM367 -66v790h39v-790h-39zM464 -66v790h19v-790h-19zM521 -66v790h79v-790h-79zM627 -66v790h40v-790h-40zM724 -66v790h20v-790h-20zM812 -66v790h19v-790h-19z
|
|
||||||
M868 -66v790h79v-790h-79z" />
|
|
||||||
<glyph glyph-name="uniE794" unicode="" horiz-adv-x="558"
|
|
||||||
d="M0 214q0 41 11.5 79t32.5 71q11 17 35 49.5t53 76t56.5 95t44.5 107.5q5 18 18 26t28 6q16 2 29 -6t18 -26q17 -56 44.5 -107.5t56.5 -95t53 -76t35 -49.5q21 -33 32.5 -71t11.5 -79q0 -58 -22 -109t-60 -89t-89 -60t-109 -22t-108.5 22t-88.5 60t-60 89t-22 109z
|
|
||||||
M122 152q0 -29 20.5 -49t50.5 -20q28 0 48.5 20t20.5 49q0 19 -10 38q-4 4 -10 12t-13 19t-14 24t-11 27q-4 10 -11 7q-10 3 -12 -7q-4 -14 -11.5 -27t-14.5 -24t-13 -19t-8 -12q-12 -18 -12 -38z" />
|
|
||||||
<glyph glyph-name="uniE7A2" unicode="" horiz-adv-x="917"
|
|
||||||
d="M842 355h-331v335q138 0 234.5 -97t96.5 -234v-4zM406 250h333v4q0 -138 -97.5 -235t-235 -97t-234.5 97t-97 234.5t97 235t234 97.5v-336z" />
|
|
||||||
<glyph glyph-name="uniE7AC" unicode="" horiz-adv-x="1224"
|
|
||||||
d="M144 329l285 122v86l-285 121v-89l188 -76l-188 -75v-89zM470 260h286v69h-286v-69zM1224 800v-900h-1224v900h1224z" />
|
|
||||||
<glyph glyph-name="uniE7B5" unicode="" horiz-adv-x="1021"
|
|
||||||
d="M475 -139v115l-150 -57l-15 26l165 101v58h-317q-19 0 -32.5 13t-13.5 32q0 17 12 29.5t30 15.5v414q-18 1 -30 13.5t-12 29.5q0 19 13.5 32t32.5 13h326l18 49h12l18 -49h332q18 0 31.5 -13t13.5 -32t-13.5 -32t-31.5 -13v-412q18 0 31.5 -13t13.5 -32t-13.5 -32
|
|
||||||
t-31.5 -13h-328v-58l166 -101l-14 -26l-152 57v-115q0 -13 -9 -22.5t-22 -9.5q-14 0 -22 9.5t-8 22.5zM806 198v404h-595v-404h595z" />
|
|
||||||
<glyph glyph-name="uniE800" unicode="" horiz-adv-x="828"
|
|
||||||
d="M0.5 423.5q4.5 13.5 26.5 17.5l251 35l111 228q10 20 24.5 20t24.5 -20l113 -228l249 -35q23 -4 27.5 -17.5t-11.5 -29.5l-181 -177l42 -248q3 -17 -2.5 -26t-16.5 -9q-7 0 -20 5l-224 118l-223 -118q-13 -5 -20 -5q-12 0 -17.5 9t-2.5 26l43 248l-181 177
|
|
||||||
q-17 16 -12.5 29.5zM143 377l105 -103l31 -28l-7 -41l-26 -147l131 70l37 19l37 -19l130 -70l-24 147l-8 41l30 28l106 103l-146 21l-41 7l-19 37l-65 132l-66 -132l-17 -37l-42 -7z" />
|
|
||||||
<glyph glyph-name="uniE801" unicode="" horiz-adv-x="828"
|
|
||||||
d="M0.5 423.5q4.5 13.5 26.5 17.5l251 36l111 227q11 21 25 21t24 -21l113 -227l249 -36q23 -4 27.5 -17.5t-11.5 -29.5l-181 -177l42 -248q4 -23 -7.5 -31.5t-31.5 3.5l-224 116l-223 -116q-22 -12 -33.5 -3.5t-6.5 31.5l43 248l-181 177q-17 16 -12.5 29.5z" />
|
|
||||||
<glyph glyph-name="uniE802" unicode=""
|
|
||||||
d="M0 377q0 72 37.5 135t101.5 110t150.5 74.5t184.5 27.5t184 -27.5t150.5 -74.5t101.5 -110t37 -134.5t-37 -135t-101.5 -110t-150.5 -74t-184 -27.5q-47 0 -89 6q-44 -33 -95 -57.5t-109 -36.5l-25 -5l-27 -4q-16 -2 -21 15v1q-2 7 4 12l9 10l21 23q10 11 17.5 25.5
|
|
||||||
t14.5 34t12 48.5q-85 47 -135.5 118t-50.5 156z" />
|
|
||||||
<glyph glyph-name="uniE803" unicode=""
|
|
||||||
d="M0 63v79q0 20 20 20h113q26 0 50.5 16t48 43t46.5 61l46 72l60 90q30 45 64.5 79.5t75.5 56.5t93 22h104v94q0 21 12.5 25.5t30.5 -10.5l172 -142q11 -10 11 -24t-11 -23l-172 -144q-18 -14 -30.5 -10t-12.5 25v91h-104q-28 0 -52 -16.5t-47.5 -43.5t-47.5 -62l-46 -71
|
|
||||||
q-28 -46 -58 -90.5t-65 -79t-76.5 -56t-91.5 -21.5h-113q-8 0 -14 5t-6 14zM0 504v79q0 8 6 14t14 6h113q54 0 98 -25t81 -65q-19 -26 -36 -52l-33 -50q-26 33 -53 53t-57 20h-113q-8 0 -14 5.5t-6 14.5zM439 134l35 51l32 51q27 -32 53.5 -52t57.5 -20h104v99q0 21 12.5 25
|
|
||||||
t30.5 -11l172 -143q11 -9 11 -23q0 -15 -11 -24l-172 -142q-18 -15 -30.5 -11t-12.5 25v87h-104q-56 0 -98.5 24t-79.5 64z" />
|
|
||||||
<glyph glyph-name="uniE804" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 -34v726q0 20 18 30q20 7 33 -8l335 -362q9 -9 9 -23t-9 -23l-335 -362q-9 -10 -22 -10q-3 0 -11 2q-18 9 -18 30zM395 -34v726q0 20 18 30q20 7 32 -8l336 -362q8 -9 8 -23t-8 -23l-336 -362q-8 -10 -21 -10q-3 0 -11 2q-18 9 -18 30z" />
|
|
||||||
<glyph glyph-name="uniE805" unicode="" horiz-adv-x="552"
|
|
||||||
d="M0 -27q0 -16 11.5 -27.5t27.5 -11.5h79q17 0 28.5 11.5t11.5 27.5v711q0 17 -11.5 28.5t-28.5 11.5h-79q-16 0 -27.5 -11.5t-11.5 -28.5v-711zM158 329q0 14 8 22l336 363q7 10 21 10q3 0 12 -2q18 -10 18 -31v-725q0 -21 -18 -30q-20 -7 -33 8l-336 362q-8 9 -8 23z" />
|
|
||||||
<glyph glyph-name="uniE806" unicode="" horiz-adv-x="552"
|
|
||||||
d="M0 -34v726q0 20 18 30q20 7 33 -8l335 -362q9 -9 9 -23t-9 -23l-335 -362q-9 -10 -22 -10q-3 0 -11 2q-18 9 -18 30zM395 -27q0 -16 11.5 -27.5t27.5 -11.5h79q16 0 28 11.5t12 27.5v711q0 17 -12 28.5t-28 11.5h-79q-16 0 -27.5 -11.5t-11.5 -28.5v-711z" />
|
|
||||||
<glyph glyph-name="uniE807" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 -30v718q0 15 10.5 25.5t25.5 10.5h718q14 0 24.5 -10.5t10.5 -25.5v-718q0 -15 -10.5 -25.5t-24.5 -10.5h-718q-15 0 -25.5 10.5t-10.5 25.5z" />
|
|
||||||
<glyph glyph-name="uniE808" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 -30v718q0 15 10.5 25.5t25.5 10.5h251q15 0 25.5 -10.5t10.5 -25.5v-718q0 -15 -10.5 -25.5t-25.5 -10.5h-251q-15 0 -25.5 10.5t-10.5 25.5zM466 -30v718q0 15 10.5 25.5t25.5 10.5h252q14 0 24.5 -10.5t10.5 -25.5v-718q0 -15 -10.5 -25.5t-24.5 -10.5h-252
|
|
||||||
q-15 0 -25.5 10.5t-10.5 25.5z" />
|
|
||||||
<glyph glyph-name="uniE809" unicode="" horiz-adv-x="693"
|
|
||||||
d="M0 -30v718q0 20 18 30q19 12 36 0l622 -357q18 -13 18 -32t-18 -32l-622 -358q-9 -5 -18.5 -5t-17.5 5q-18 11 -18 31z" />
|
|
||||||
<glyph glyph-name="uniE80A" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 433v230q0 25 18 43t43 18h230q25 0 55.5 -13t48.5 -30l377 -377q17 -18 17 -43.5t-17 -43.5l-266 -265q-18 -18 -43 -18t-43 18l-377 377q-18 18 -30.5 48.5t-12.5 55.5zM99 566q0 -24 17.5 -41.5t41.5 -17.5t41.5 17.5t17.5 41.5t-17.5 41.5t-41.5 17.5t-41.5 -17.5
|
|
||||||
t-17.5 -41.5z" />
|
|
||||||
<glyph glyph-name="uniE80B" unicode=""
|
|
||||||
d="M0 -26v139q0 17 11.5 28t28.5 11h153q15 0 27 -11t12 -28v-139q0 -17 -12 -28.5t-27 -11.5h-153q-17 0 -28.5 11.5t-11.5 28.5zM0 271v116q0 17 11.5 28t28.5 11h153q15 0 27 -11t12 -28v-116q0 -17 -12 -28.5t-27 -11.5h-153q-17 0 -28.5 11.5t-11.5 28.5zM0 545v140
|
|
||||||
q0 16 11.5 27.5t28.5 11.5h153q15 0 27 -11.5t12 -27.5v-140q0 -17 -12 -28.5t-27 -11.5h-153q-17 0 -28.5 11.5t-11.5 28.5zM311 -26v139q0 17 11.5 28t28.5 11h557q16 0 27.5 -11t11.5 -28v-139q0 -17 -11.5 -28.5t-27.5 -11.5h-557q-17 0 -28.5 11.5t-11.5 28.5zM311 271
|
|
||||||
v116q0 17 11.5 28t28.5 11h557q16 0 27.5 -11t11.5 -28v-116q0 -17 -11.5 -28.5t-27.5 -11.5h-557q-17 0 -28.5 11.5t-11.5 28.5zM311 545v140q0 16 11.5 27.5t28.5 11.5h557q16 0 27.5 -11.5t11.5 -27.5v-140q0 -17 -11.5 -28.5t-27.5 -11.5h-557q-17 0 -28.5 11.5
|
|
||||||
t-11.5 28.5z" />
|
|
||||||
<glyph glyph-name="uniE80C" unicode=""
|
|
||||||
d="M0 -26v139q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-139q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM0 271v116q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-116q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5
|
|
||||||
t-11.5 28.5zM0 545v140q0 16 11.5 27.5t28.5 11.5h184q16 0 27.5 -11.5t11.5 -27.5v-140q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM342 -26v139q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-139q0 -17 -11.5 -28.5t-27.5 -11.5h-184
|
|
||||||
q-17 0 -28.5 11.5t-11.5 28.5zM342 271v116q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-116q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM342 545v140q0 16 11.5 27.5t28.5 11.5h184q16 0 27.5 -11.5t11.5 -27.5v-140q0 -17 -11.5 -28.5
|
|
||||||
t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM684 -26v139q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-139q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM684 271v116q0 17 11.5 28t28.5 11h184q16 0 27.5 -11t11.5 -28v-116
|
|
||||||
q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5zM684 545v140q0 16 11.5 27.5t28.5 11.5h184q16 0 27.5 -11.5t11.5 -27.5v-140q0 -17 -11.5 -28.5t-27.5 -11.5h-184q-17 0 -28.5 11.5t-11.5 28.5z" />
|
|
||||||
<glyph glyph-name="uniE80D" unicode="" horiz-adv-x="789"
|
|
||||||
d="M0 33q0 20 15 35l261 262l-261 257q-15 15 -15 35t15 34l51 52q15 15 35 15t35 -15l259 -259l259 259q14 15 34 15t35 -15l52 -52q14 -14 14 -34t-14 -35l-261 -262l261 -257q14 -15 14 -34.5t-14 -34.5l-52 -52q-15 -15 -35 -15t-34 15l-259 260l-260 -260
|
|
||||||
q-15 -15 -34.5 -15t-34.5 15l-51 51q-15 15 -15 35z" />
|
|
||||||
<glyph glyph-name="uniE80E" unicode=""
|
|
||||||
d="M1 339q3 12 14 20l433 356q12 9 25.5 9t25.5 -9l133 -109v61q0 20 20 20h117q20 0 20 -20v-190l144 -118q10 -8 13 -20t-1 -24q-10 -25 -38 -25h-79v-316q0 -17 -11 -28.5t-28 -11.5h-216v237h-198v-237h-217q-17 0 -28.5 11.5t-11.5 28.5v316h-79q-26 0 -37 25
|
|
||||||
q-4 12 -1 24z" />
|
|
||||||
<glyph glyph-name="uniE80F" unicode=""
|
|
||||||
d="M0 305.5q0 22.5 13 42.5q40 64 91.5 115t111 86.5t124.5 54t134 18.5t134.5 -18.5t124.5 -53.5t110.5 -86t92.5 -116q11 -20 11 -42.5t-11 -41.5q-41 -66 -92.5 -117t-110.5 -85.5t-124.5 -53t-134.5 -18.5t-134 18.5t-124.5 53.5t-111 86t-91.5 116q-13 19 -13 41.5z
|
|
||||||
M79 306q34 -54 77 -98t93.5 -74.5t107 -47.5t117.5 -17t117 17t106.5 47.5t94 74.5t76.5 98q-41 66 -96 116t-121 81q26 -31 40.5 -69t14.5 -82q0 -49 -18.5 -92t-51 -75.5t-75.5 -51t-92 -18.5t-91.5 18.5t-75.5 51t-51 75.5t-18 92q0 40 13 76.5t35 65.5
|
|
||||||
q-60 -30 -111.5 -78t-90.5 -110zM316 352q0 -11 8.5 -20t21 -9t21 9t8.5 20q0 40 27 67.5t67 27.5q13 0 21.5 9t8.5 20q0 13 -8.5 21.5t-21.5 8.5q-31 0 -59.5 -12t-49 -32.5t-32.5 -49t-12 -60.5z" />
|
|
||||||
<glyph glyph-name="uniE810" unicode=""
|
|
||||||
d="M0 -7v672q0 24 17.5 41.5t41.5 17.5h356q24 0 41.5 -17.5t17.5 -41.5t17 -41.5t42 -17.5h355q25 0 42 -18t17 -42v-553q0 -24 -17 -41.5t-42 -17.5h-829q-24 0 -41.5 17.5t-17.5 41.5z" />
|
|
||||||
<glyph glyph-name="uniE811" unicode="" horiz-adv-x="530"
|
|
||||||
d="M265 700q103 0 176.5 -73t73.5 -177q0 -67 -25.5 -148t-62.5 -149.5t-74 -127t-63 -91.5l-25 -34l-27 35q-17 22 -60 89t-76 130t-60 146t-27 150q0 104 73.5 177t176.5 73zM265 312q56 0 95.5 39.5t39.5 95.5t-39.5 95.5t-95.5 39.5t-95.5 -39.5t-39.5 -95.5t39.5 -95.5
|
|
||||||
t95.5 -39.5z" />
|
|
||||||
<glyph glyph-name="uniE812" unicode="" horiz-adv-x="1030"
|
|
||||||
d="M871 466h-100l-124 151l-214 -151h-180q-53 0 -90.5 -38t-37.5 -92v-159l-107 297q-6 16 0.5 31t21.5 20l680 248q15 5 29 -2t20 -23zM977 376q16 0 27 -11.5t11 -28.5v-471q0 -17 -11 -28.5t-27 -11.5h-724q-16 0 -27 11.5t-11 28.5v471q0 17 11 28.5t27 11.5h724z
|
|
||||||
M922 -75v161l-73 161l-167 -60l-129 -133l-139 172l-92 -215v-86h600z" />
|
|
||||||
<glyph glyph-name="uniE813" unicode=""
|
|
||||||
d="M0 665v39q0 20 20 20h119q8 0 19 -2.5t19 -4.5q3 -2 7 -7t8 -12t6.5 -14t3.5 -11l14 -64h692q18 0 29.5 -13.5t8.5 -30.5l-55 -297q-4 -13 -14.5 -22t-24.5 -9h-557l18 -86q2 -8 8.5 -13.5t15.5 -5.5h440q8 0 14 -5.5t6 -14.5v-40q0 -8 -6 -13.5t-14 -5.5h-84h-335h-54
|
|
||||||
q-8 0 -18.5 1.5t-18.5 4.5q-3 2 -7 7.5t-8 12.5t-6.5 13.5t-3.5 11.5l-110 522q-3 8 -9.5 13.5t-15.5 5.5h-87q-20 0 -20 20zM299 -7q0 24 17.5 42t41.5 18q25 0 42.5 -18t17.5 -42t-17.5 -41.5t-42.5 -17.5q-24 0 -41.5 17.5t-17.5 41.5zM634 -7q0 24 17 42t41.5 18t42 -18
|
|
||||||
t17.5 -42t-17.5 -41.5t-42 -17.5t-41.5 17.5t-17 41.5z" />
|
|
||||||
<glyph glyph-name="uniE814" unicode="" horiz-adv-x="1228"
|
|
||||||
d="M31 151q0 46 82 97q66 36 143 46v51q-77 77 -77 190q0 194 154 194q77 0 97 -20q26 -20 36 -92q-97 -66 -98 -246q0 -159 67 -230q-26 0 -71 -31l-36 -20l-11 -10q-15 -11 -25 -21l-26 -26q-25 0 -138 31q-97 31 -97 87zM281 -43.5q0 40.5 93 97.5q87 46 158 51v56
|
|
||||||
q-82 77 -81 205q0 210 163.5 210t163.5 -210q0 -128 -81 -205v-56q71 -6 158 -51.5t87 -96.5q0 -41 -71 -62q-77 -21 -149 -31q-46 -5 -107.5 -5t-107.5 5q-71 10 -148.5 31t-77.5 61.5zM763 617q10 72 36 92q20 20 97 20q153 0 153 -194q0 -113 -76 -190v-51
|
|
||||||
q76 -10 143 -46q87 -46 87 -97t-102 -87q-113 -31 -139 -31l-25 26q-10 10 -26 21l-10 10q-20 10 -36 20q-46 31 -71 31q66 71 66 230q0 180 -97 246z" />
|
|
||||||
<glyph glyph-name="uniE815" unicode="" horiz-adv-x="1228"
|
|
||||||
d="M358 341l384 414l-128 -358l257 -149l-384 -414l127 358z" />
|
|
||||||
<glyph glyph-name="uniE817" unicode="" horiz-adv-x="961"
|
|
||||||
d="M481 722q169 0 287 -117t118 -286q0 -170 -117.5 -288.5t-287.5 -118.5q-169 0 -287.5 118.5t-118.5 288.5q0 167 118.5 285t287.5 118zM504 -11q67 0 116 169q-58 -6 -116 -8v-161zM375 70q34 -81 81 -81v161q-58 2 -115 8q11 -48 34 -88zM152 272q34 -40 133 -57
|
|
||||||
q-7 46 -7 100q0 33 3 69q-82 16 -117 31q-14 -47 -14 -96q0 -33 2 -47zM322 315q0 -57 8 -109q60 -10 126 -12v176q-76 2 -132 9q0 -11 -1 -32t-1 -32zM454 646q-43 -16 -76 -77t-48 -148q61 -5 126 -7v232h-2zM536 629q-26 17 -32 17v-232q62 1 127 7q-26 145 -95 208z
|
|
||||||
M504 194q64 1 126 11q8 40 8 110q0 41 -2 63q-64 -7 -132 -8v-176zM680 384q2 -24 2 -69q0 -49 -6 -101q90 15 112.5 36.5t22.5 68.5q0 50 -15 95q-57 -24 -116 -30zM779 458q-57 123 -179 167q54 -70 74 -199q84 15 105 32zM248 550q-40 -36 -66 -91q6 -5 104 -31
|
|
||||||
q22 124 75 196q-80 -38 -113 -74zM248 85q40 -44 106 -71q-40 56 -60 151q-84 19 -125 41q31 -77 79 -121zM714 85q48 45 75 116q-47 -21 -121 -37q-23 -96 -62 -150q68 28 108 71z" />
|
|
||||||
<glyph glyph-name="uniF301" unicode="" horiz-adv-x="978"
|
|
||||||
d="M188 800h602q73 0 123.5 -51t50.5 -123v-602q0 -72 -50.5 -123t-123.5 -51h-602q-72 0 -123 51t-51 123v602q0 72 51 123t123 51zM515 -150h141v371h139l6 134h-145v97q0 32 10.5 46t45.5 14l85 -1l4 126q-41 4 -95 4q-95 0 -143 -49t-48 -123v-114h-99v-134h99v-371z
|
|
||||||
" />
|
|
||||||
<glyph glyph-name="uniF303" unicode="" horiz-adv-x="1332"
|
|
||||||
d="M1318 385q-35 -5 -77.5 -0.5t-69.5 15.5q53 4 90.5 27t51.5 57q-22 -14 -69.5 -21t-84.5 4q-1 4 -3 14.5t-4 16.5q-26 97 -105.5 161.5t-170.5 54.5l29 11q2 1 14.5 4t24 6.5t23.5 9t17.5 12t1.5 14.5q-2 7 -14.5 7.5t-29.5 -4t-32.5 -9.5t-31 -11t-18.5 -7q64 24 69 53
|
|
||||||
q-59 -8 -103 -49q17 19 20 39q-68 -43 -115 -122.5t-89 -192.5q-62 59 -99 79q-136 73 -335 148q-4 -42 23.5 -87.5t88.5 -77.5q-27 4 -81 -9q9 -48 43 -81t104 -49q-61 -4 -90 -35q19 -37 59.5 -61.5t101.5 -18.5q-35 -15 -46.5 -36.5t-2 -39t34.5 -28.5t55 -6
|
|
||||||
q-68 -71 -158.5 -94.5t-177 -1.5t-148.5 82q79 -108 191.5 -172.5t229 -80t237 6.5t219.5 77.5t172.5 142.5t99.5 193q101 -1 155 59z" />
|
|
||||||
<glyph glyph-name="uniF308" unicode="" horiz-adv-x="1132"
|
|
||||||
d="M456 224q0 51 -22 87.5t-52.5 36.5t-52 -36.5t-21.5 -87.5t21.5 -87t52.5 -36t52.5 36t21.5 87zM856 224q0 51 -22 87.5t-52.5 36.5t-53 -36.5t-22.5 -87.5t22 -87t53.5 -36t53 36t21.5 87zM933 238q0 -37 -4.5 -68.5t-14.5 -55t-21 -42.5t-29.5 -32.5t-35 -23.5
|
|
||||||
t-42.5 -16t-46.5 -10t-52 -6t-54.5 -2.5t-58 -0.5q-65 0 -114 5.5t-97 21.5t-78 43.5t-49 74.5t-19 112q0 78 61 142q23 24 58.5 33.5t65.5 8.5t84.5 -5.5t87.5 -4.5t87 4.5t85 5.5t66.5 -8.5t57.5 -32.5q62 -67 62 -143zM724 -99q156 15 239.5 64.5t120.5 136.5q6 12 19 59
|
|
||||||
q16 52 16 174q0 146 -102 248q31 101 -15 216q-3 1 -9 2t-27 -1t-45.5 -9t-65.5 -27t-85 -50q-91 23 -198 23q-115 0 -218 -28q-47 32 -89 53t-68.5 28t-46.5 10t-28 1l-9 -2q-21 -54 -24 -108.5t2.5 -79.5t12.5 -43q-90 -98 -90 -233q0 -115 27 -193q2 -8 22 -48
|
|
||||||
q87 -162 364 -193h297z" />
|
|
||||||
<glyph glyph-name="u1D30D" unicode="𝌍" horiz-adv-x="1228"
|
|
||||||
d="M154 294q0 190 133 323q138 138 327 138q195 0 323 -138q138 -128 138 -323q0 -189 -138 -327q-133 -133 -323 -133q-184 0 -327 133q-133 143 -133 327zM251 494h30q26 -30 32 -30q25 -93 55 -108l26 -15q16 0 16 25q0 20 -10 31q0 20 4 25q11 21 21 26q10 20 15 20
|
|
||||||
l11 -15q15 -30 30 -36v-10q6 -15 6 -20v-10q0 -6 -6 -6q0 -5 -8 -5h-12q5 0 5 -10q6 -11 10 -15l11 -26l15 -26q5 -10 15 -20q16 -31 36 -36q21 -15 56 -15v-21l11 -5q5 0 5 -5q20 0 30 5l6 -5q-6 -20 -47 -41l-40 -15v-11q0 -5 -3 -15t-3 -25q-10 -57 -15 -62q26 -6 41 -5
|
|
||||||
q10 0 41 10l41 10q15 -10 25 -25v-15l16 -6v-20l-5 -6l5 -10h15q31 0 36 36q41 16 61 46q21 26 21 72l-5 5q-21 36 -21 46q-15 31 -15 41q10 -15 30 -15h6l26 5l-16 25v6l20 -21l-10 -26l26 16l15 -5q16 5 26 30h-10v26l-16 -10l-5 5v5q-15 0 -20 5q-6 0 -6 11v4
|
|
||||||
q-36 21 -46 21v-21v-10h-5q-25 31 -56 31h-20q-11 10 -21 10h-10v16l-10 5q0 5 -3 15t-3 20q-5 21 -15 32l5 10h-20q-10 36 -21 35l5 26v20l-15 6q-26 0 -26 -26l16 -31q-10 -20 -10 -25q0 -16 5 -21l-5 -10q-10 -10 -11 -20v-6l-46 -35q-5 0 -5 10v10v10q0 16 -15 36l15 -5
|
|
||||||
q16 0 26 36v20q0 21 -6 26q11 0 11 25q56 11 56 72q0 16 -15 46l-10 10h5q15 -26 30 -25q6 0 16 10q5 10 5 21q0 15 -11 25l-4 5v6q20 10 20 15q5 5 5 15v31q-51 10 -72 10q-225 0 -363 -215zM650 305v20q0 -5 11 -15zM676 238v16q0 10 5 15v-31h20v5v-5h6q10 0 15 5l10 -5
|
|
||||||
q16 -5 16 -15q0 -5 -6 -11h-10q-20 0 -30.5 -10t-15.5 -10l-5 5q20 0 20 21v15h-15l-5 5h-5zM758 207q5 -5 10 -5v-10q-5 -10 -5 -15v10q-5 5 -5 20zM891 141q0 -16 15 -31q10 -10 26 -10l10 -5q10 0 10 -5q-15 51 -25 56q-16 16 -31 25q0 -10 -5 -20v-10z" />
|
|
||||||
<glyph glyph-name="u1F304" unicode="🌄"
|
|
||||||
d="M0 -7v672q0 24 17.5 41.5t41.5 17.5h829q25 0 42 -17.5t17 -41.5v-672q0 -24 -17 -41.5t-42 -17.5h-829q-24 0 -41.5 17.5t-17.5 41.5zM79 13h789v632h-789v-632zM158 92v54l142 185l97 -80l182 276l210 -218v-217h-631zM158 486q0 33 23 56.5t57 23.5q33 0 56 -23.5
|
|
||||||
t23 -56.5t-23 -56t-56 -23q-34 0 -57 23t-23 56z" />
|
|
||||||
<glyph glyph-name="u1F310" unicode="🌐" horiz-adv-x="1228"
|
|
||||||
d="M307 -115q0 51 51 51h205v61q-127 16 -235 113q-11 10 -11 36q0 21 11 31q15 15 30 15q21 0 36 -15q72 -72 169 -87h102q98 15 170 87q92 92 92 220t-92 220l-31 36l138 133q10 15 30.5 15t31.5 -15q15 -10 15 -30.5t-15 -30.5l-72 -77q87 -113 87 -251q0 -159 -118 -287
|
|
||||||
q-107 -97 -236 -113v-61h206q51 0 51 -51t-51 -51h-513q-51 0 -51 51zM364 396.5q0 102.5 74 177t176.5 74.5t176.5 -74.5t74 -177t-74 -176.5t-176.5 -74t-176.5 74t-74 176.5z" />
|
|
||||||
<glyph glyph-name="u1F381" unicode="🎁"
|
|
||||||
d="M0 230v198q0 8 6 14t14 6h272q-29 0 -54 10.5t-44 29.5t-30 44t-11 53.5t11 54t30 44t44 29.5t54 11q30 0 57.5 -12.5t45.5 -35.5l79 -102l79 102q18 23 45 35.5t58 12.5q28 0 53.5 -11t44.5 -29.5t30 -44t11 -54t-11 -53.5t-30 -44t-44.5 -29.5t-53.5 -10.5h271
|
|
||||||
q9 0 14.5 -6t5.5 -14v-198q0 -8 -5.5 -13.5t-14.5 -5.5h-59v-218q0 -24 -17 -41.5t-42 -17.5h-671q-24 0 -41.5 17.5t-17.5 41.5v218h-59q-8 0 -14 5.5t-6 13.5zM232 586q0 -24 17.5 -41.5t42.5 -17.5h119l-77 99q-6 5 -16.5 12t-25.5 7q-25 0 -42.5 -17.5t-17.5 -41.5z
|
|
||||||
M375 45q0 -16 11.5 -27.5t28.5 -11.5h118q16 0 28 11.5t12 27.5v403h-198v-403zM536 527h120q24 0 42 17.5t18 41.5t-18 41.5t-42 17.5q-16 0 -26.5 -7t-15.5 -12z" />
|
|
||||||
<glyph glyph-name="u1F3A4" unicode="🎤" horiz-adv-x="670"
|
|
||||||
d="M635 438q8 0 14 -6t6 -14v-138q0 -93 -69 -165t-201 -83v-132h130q8 0 14 -6t6 -14v-60q0 -8 -6 -14t-14 -6h-360q-8 0 -14 6t-6 14v60q0 8 6 14t14 6h130v132q-132 11 -201 83t-69 165v138q0 8 6 14t14 6h30q8 0 14 -6t6 -14v-138q0 -28 12 -57t38.5 -58t78 -47
|
|
||||||
t121.5 -18t121.5 18t78 47t38.5 58t12 57v138q0 8 6 14t14 6h30zM335 200q-79 0 -114.5 25t-35.5 55v158h300v-158q0 -30 -35.5 -55t-114.5 -25zM485 720v-212h-300v212q0 30 35.5 55t114.5 25t114.5 -25t35.5 -55z" />
|
|
||||||
<glyph glyph-name="u1F3A7" unicode="🎧"
|
|
||||||
d="M0 309q0 57 19.5 110t52.5 99t79 83.5t98.5 65t110 42.5t114.5 15t114 -15t110 -42.5t98.5 -65t78.5 -83.5t52.5 -99t19.5 -110q0 -92 -38 -174l-13 -29l-85 -13q-14 -51 -55.5 -85t-97.5 -34v-20q0 -8 -6 -14t-14 -6h-40q-9 0 -14 6t-5 14v355q0 8 5 14t14 6h40
|
|
||||||
q8 0 14 -6t6 -14v-19q44 0 79.5 -22t56.5 -57l20 2q15 45 15 96q0 61 -32.5 115t-84 94t-114.5 64t-124 24t-124.5 -24t-114.5 -64t-83.5 -94t-32.5 -115q0 -48 15 -96l20 -2q21 35 56 57t79 22v19q0 8 6 14t14 6h40q9 0 14 -6t5 -14v-355q0 -8 -5 -14t-14 -6h-40
|
|
||||||
q-8 0 -14 6t-6 14v20q-55 0 -96.5 34t-55.5 85l-85 13l-14 29q-38 82 -38 174z" />
|
|
||||||
<glyph glyph-name="u1F3C6" unicode="🏆" horiz-adv-x="930"
|
|
||||||
d="M524 82v-65q71 -8 117 -32t46 -55q0 -37 -65 -63.5t-157 -26.5t-157 26.5t-65 63.5q0 31 46 55t117 32v65q0 50 -33 85t-112 87q-45 30 -69 47.5t-63.5 54.5t-60 72t-37 88.5t-16.5 115.5q0 14 10 24.5t25 10.5h172q21 39 81.5 66t161.5 27t161.5 -27t81.5 -66h172
|
|
||||||
q15 0 25 -10.5t10 -24.5q0 -62 -16.5 -115.5t-37 -88.5t-60 -72t-63.5 -54.5t-69 -47.5q-79 -52 -112 -87t-33 -85zM663 335q80 54 126 112t54 150h-126q-5 -157 -54 -262zM465 699q-39 0 -72 -6t-54 -15t-36 -20t-21.5 -20t-6.5 -16t6.5 -16t21.5 -20t36 -20t54 -15t72 -6
|
|
||||||
t72 6t54 15t36 20t21.5 20t6.5 16t-6.5 16t-21.5 20t-36 20t-54 15t-72 6zM87 597q8 -92 54 -150t126 -112q-49 105 -54 262h-126z" />
|
|
||||||
<glyph glyph-name="u1F3C9" unicode="🏉" horiz-adv-x="868"
|
|
||||||
d="M0 470v96q0 17 11.5 28t28.5 11h166q-1 6 -1 13v12v2q0 27 2 45t8 28t16.5 14.5t28.5 4.5h348q17 0 28 -4.5t17 -14.5t8 -28t2 -45v-14q0 -6 -1 -13h166q17 0 28.5 -11t11.5 -28v-96q0 -33 -23 -67.5t-62.5 -65.5t-92.5 -55t-114 -33q-27 -5 -47.5 -21.5t-20.5 -36.5
|
|
||||||
q0 -18 9 -26.5t20 -15.5t20.5 -16t11.5 -27q3 -12 -1 -24q-2 -8 12.5 -12.5t35 -9.5t42 -11.5t33.5 -17.5q6 -5 9.5 -20.5t4.5 -33.5q1 -16 -3 -29.5t-15 -13.5h-506q-10 0 -14.5 13.5t-3.5 29.5q1 18 5 33.5t10 20.5q11 11 32.5 17.5t42 11.5t35 9.5t12.5 12t-2 12.5v12
|
|
||||||
q1 18 11 27t21.5 16t20.5 15.5t9 26.5q0 20 -20.5 36.5t-47.5 21.5q-60 10 -113.5 34t-93 54.5t-62.5 65t-23 67.5zM79 470q0 -11 12 -28t34.5 -36t55 -37t72.5 -31q-13 41 -23 90t-17 98h-134v-56zM616 338q40 13 72 31t54.5 37t34.5 36t12 28v56h-134q-7 -49 -17 -98
|
|
||||||
t-22 -90z" />
|
|
||||||
<glyph glyph-name="u1F44D" unicode="👍" horiz-adv-x="824"
|
|
||||||
d="M0.5 168q-1.5 59 2 118t13.5 110q47 3 98.5 3t94.5 -10q7 -40 11 -96t5.5 -116t0.5 -116.5t-5 -98.5q-19 -3 -43.5 -3.5t-51.5 1t-53 2t-47 0.5q-10 40 -17 93.5t-8.5 112.5zM96 41q0 -17 11.5 -28.5t28.5 -11.5t28 11.5t11 28.5q0 15 -11 26.5t-28 11.5t-28.5 -11.5
|
|
||||||
t-11.5 -26.5zM253 368q23 10 37 20t25.5 22.5t24 27.5t32.5 36q16 17 29.5 28t24.5 21t20 21t18 27q17 31 22.5 68.5t13.5 71.5q0 7 7 12q20 3 37 -4t29.5 -18.5t20.5 -27.5t11 -29q7 -35 -1 -63t-20 -53l-24 -49q-11 -24 -12 -50q23 -10 55 -10.5t66.5 1.5t67 2t55 -9
|
|
||||||
t30 -30.5t-5.5 -63.5q0 -2 -2 -5l-6 -8q-3 -5 -5 -10l-2 -3q12 -11 17 -24t5 -21q1 -41 -34 -72q11 -15 12 -32.5t-4 -33.5t-15 -28t-23 -18q6 -36 -6.5 -61.5t-37 -40.5t-59.5 -21.5t-73 -6.5t-76.5 5.5t-70.5 14.5q-21 7 -41 15l-41 15q-20 8 -43 12.5t-48 -0.5
|
|
||||||
q2 41 2.5 89.5t-1 98.5t-4.5 97.5t-7 86.5z" />
|
|
||||||
<glyph glyph-name="u1F44E" unicode="👎" horiz-adv-x="824"
|
|
||||||
d="M1 410q-1 22 8 39.5t26 32.5q-10 15 -11 32.5t4 33.5t15 28t22 18q-6 36 6.5 61.5t37.5 40.5t59.5 21.5t72.5 6.5t76.5 -5.5t70.5 -14.5q21 -7 41 -15t41 -15.5t43.5 -11.5t47.5 1q-2 -41 -2.5 -90t1 -99t4 -97.5t8.5 -86.5q-24 -11 -38 -21t-25 -22t-23.5 -27t-32.5 -35
|
|
||||||
q-16 -18 -29 -29l-24 -21q-12 -10 -21.5 -20.5t-18.5 -27.5q-17 -31 -21.5 -68.5t-14.5 -71.5q0 -9 -7 -12q-21 -3 -37.5 4t-29 18.5t-20.5 26.5t-11 30q-6 35 1.5 63t20 53t23.5 49.5t12 50.5q-22 9 -54.5 9t-67 -1.5t-66.5 -1.5t-54.5 9t-30.5 30.5t5 62.5q1 1 4 6l6 9
|
|
||||||
q2 4 3 9l2 3q-11 11 -16.5 24t-5.5 21zM598 597.5q1 56.5 5 98.5q19 2 44.5 2.5t52 0t52.5 -1.5t47 -1q10 -40 16.5 -93.5t8 -112t-2 -118t-13.5 -110.5q-47 -4 -98 -4t-95 11q-7 40 -11 96t-5.5 116t-0.5 116.5zM649 618q0 -16 12 -28t28 -12t27.5 12t11.5 28t-11.5 27.5
|
|
||||||
t-27.5 11.5t-28 -11.5t-12 -27.5z" />
|
|
||||||
<glyph glyph-name="u1F464" unicode="👤" horiz-adv-x="970"
|
|
||||||
d="M751 78q94 -34 149 -67t55 -56v-105h-940v105q0 23 55 56t149 67q93 34 128 69t35 95q0 20 -10 31.5t-24 31.5t-21 58q-2 12 -9 17.5t-14.5 8t-14 17t-8.5 43.5q0 15 4.5 25.5t8.5 13.5l5 3q-9 50 -13 88q-2 21 6 47.5t27.5 55.5t62.5 48.5t103 19.5t103 -19.5
|
|
||||||
t62.5 -48.5t27.5 -55.5t6 -47.5l-13 -88q18 -8 18 -42q-2 -29 -8.5 -43.5t-14 -17t-14.5 -8t-9 -17.5q-7 -38 -21 -58t-24 -31.5t-10 -31.5q0 -60 35 -95t128 -69z" />
|
|
||||||
<glyph glyph-name="u1F465" unicode="👥" horiz-adv-x="1030"
|
|
||||||
d="M1015 -125h-225v144q0 52 -30 78.5t-154 85.5q41 29 41 81q0 14 -13.5 31.5t-18.5 49.5q-1 8 -5.5 11.5t-8.5 5t-8.5 11.5t-5.5 29q0 10 2.5 17t5.5 9l3 2q-6 33 -8 59q-1 14 3.5 31.5t16.5 37t38 32.5t62 13t62 -13t37.5 -32.5t16 -37t3.5 -31.5l-7 -59q10 -6 10 -28
|
|
||||||
q-1 -19 -5.5 -29t-8.5 -11.5t-8.5 -5t-5.5 -11.5q-5 -32 -18.5 -49.5t-13.5 -31.5q0 -40 21 -63.5t77 -46.5q61 -25 90 -41t41 -36q5 -8 8.5 -58.5t4.5 -96.5zM526 127q89 -36 136.5 -64t47.5 -56v-132h-695v176q0 20 15.5 36t28.5 22.5t35 15.5q3 1 5 2q75 30 103 61t28 85
|
|
||||||
q0 19 -18.5 42t-25.5 66q-2 10 -7.5 15t-11.5 7t-11 15t-7 39q0 13 3.5 22.5t7.5 12.5l4 2q-8 45 -11 79q-2 19 4.5 42t22 49t50.5 43.5t83 17.5t82.5 -17.5t50 -43.5t22 -49t4.5 -42l-10 -79q14 -7 14 -37q-2 -26 -7.5 -39t-11 -15t-11 -7t-7.5 -15q-7 -43 -25 -66t-18 -42
|
|
||||||
q0 -54 28 -85t102 -61z" />
|
|
||||||
<glyph glyph-name="u1F4A1" unicode="💡" horiz-adv-x="670"
|
|
||||||
d="M473 10h-265q0 59 -21.5 114.5t-50.5 97.5l-59 86q-29 43 -47 97t-14 112q3 43 14.5 81.5t36 76t59 64.5t88.5 44t121 17t121 -17t88.5 -44t59 -64.5t36 -76t14.5 -81.5q4 -59 -12.5 -113t-43.5 -98l-56 -86q-29 -44 -49 -98t-20 -112zM203 -159v99h264v-99
|
|
||||||
q-60 -42 -132 -41q-72 -1 -132 41z" />
|
|
||||||
<glyph glyph-name="u1F4B0" unicode="💰" horiz-adv-x="803"
|
|
||||||
d="M312 349q17 20 66 23v-84q-43 3 -59.5 10t-16.5 28q0 13 10 23zM420 108v91q48 -4 63 -10.5t15 -24.5q0 -45 -78 -56zM490 351q22 -12 29 -19.5t7 -17.5l8 -4l45 90l-7 5q-10 -7 -13 -7q-8 0 -13 3q-90 35 -126 35v18q0 14 20 18v9h-79v-9q17 -4 17 -18v-15
|
|
||||||
q-73 -3 -113 -35.5t-40 -87.5t33.5 -78.5t119.5 -31.5v-96q-56 5 -86.5 23.5t-30.5 40.5l-7 4l-42 -94l7 -4q9 5 12 5q7 0 9 -3q72 -36 138 -40v-18q0 -15 -17 -20v-9h79v9q-20 5 -20 20v18q72 4 115.5 38.5t43.5 88.5q0 101 -149 115h-10v87q36 -2 70 -20zM531 532
|
|
||||||
q89 -37 143.5 -118.5t54.5 -181.5q0 -136 -95.5 -231.5t-231.5 -95.5t-231.5 95.5t-95.5 231.5q0 100 54.5 181.5t142.5 118.5l-81 179q0 25 28 25h366q27 0 27 -25z" />
|
|
||||||
<glyph glyph-name="u1F4B5" unicode="💵" horiz-adv-x="1293"
|
|
||||||
d="M79 291q76 0 149 24t134.5 62t121.5 85l120 92q61 46 122.5 84t134.5 62.5t149 24.5l105 -227l90 -192q-103 0 -199.5 -42t-176.5 -102l-160 -122q-80 -62 -182.5 -110.5t-212.5 -57.5l-98 210zM199 225l63 -137l64 -136q132 26 283 137q-70 27 -107 103q-32 72 -20 168
|
|
||||||
q-154 -112 -283 -135zM1085 372l-68 145l-59 128q-136 -25 -284 -137q67 -24 106 -103q34 -75 20 -167q151 111 285 134zM428 68q-4 6 -10 18t-8 18q-5 -3 -10 -3q1 -11 6 -40q15 3 22 7zM455 90q4 6 11 21q-24 7 -38 13q0 -2 -1.5 -3.5t-3 -3.5t-1.5 -3zM371 68q1 3 6 15.5
|
|
||||||
t8 18.5q-5 3 -9 6q-4 -6 -13 -15t-13 -14q12 -10 21 -11zM468 148q-3 14 -6 22h-1q-15 -10 -35 -20v-1q3 -3 3 -10q21 3 39 9zM329 106q27 10 37 15q-2 8 -2 9q-14 -1 -43 -1q1 -11 8 -23zM416 161q4 5 12 17l12 17q-10 9 -19 10q-10 -25 -13 -39q1 0 4 -2t4 -3zM365 145
|
|
||||||
q3 3 5 10l-16 13l-15 12q-8 -8 -11 -20q3 -1 11.5 -4.5t14.5 -5.5t11 -5zM392 168q0 7 -2 20t-2 20q-8 -2 -23 -7q3 -3 5 -9t4 -10q-2 4 -4 10t-5 9q3 -6 8.5 -18.5t8.5 -18.5q2 1 10 4zM872 404q-8 22 -43 94q-13 -7 -19 -12q8 -16 22 -47l20 -47q12 8 20 12zM960 446
|
|
||||||
q-5 13 -20.5 44t-23.5 49q-4 -1 -10.5 -4.5t-9.5 -4.5l44 -91q11 3 20 7zM916 426q-6 15 -14 30l-16 35l-15 29q-3 -2 -9 -5l-10 -5q26 -51 44 -92q4 1 11 4t9 4z" />
|
|
||||||
<glyph glyph-name="u1F4C2" unicode="📂"
|
|
||||||
d="M0 129v536q0 24 17.5 41.5t41.5 17.5h356q24 0 41.5 -17.5t17.5 -41.5t17 -41.5t42 -17.5h227q24 0 41.5 -18t17.5 -42v-114h-632q-19 0 -36.5 -6.5t-33.5 -17.5t-27.5 -26t-17.5 -33zM22 -66l126 391q4 11 16.5 19.5t23.5 8.5h759l-133 -389q-3 -12 -16 -21t-24 -9h-752
|
|
||||||
z" />
|
|
||||||
<glyph glyph-name="u1F4C4" unicode="📄" horiz-adv-x="631"
|
|
||||||
d="M0 -7v672q0 24 17.5 41.5t41.5 17.5h230v-283q0 -25 17.5 -42t41.5 -17h284v-389q0 -24 -17.5 -41.5t-41.5 -17.5h-514q-24 0 -41.5 17.5t-17.5 41.5zM348 441v280h4l276 -277v-3h-280z" />
|
|
||||||
<glyph glyph-name="u1F4C5" unicode="📅" horiz-adv-x="789"
|
|
||||||
d="M0 -7v578q0 24 17.5 41.5t41.5 17.5h38v-73q0 -29 20.5 -49.5t49.5 -20.5h16q30 0 51 20.5t21 49.5v73h61v-73q0 -29 20.5 -49.5t49.5 -20.5h17q30 0 50.5 20.5t20.5 49.5v73h61v-73q0 -29 21 -49.5t50 -20.5h16q30 0 50.5 20.5t20.5 49.5v73h38q24 0 41 -17.5t17 -41.5
|
|
||||||
v-578q0 -24 -17 -41.5t-41 -17.5h-672q-24 0 -41.5 17.5t-17.5 41.5zM79 13h632v419h-632v-419zM136 557v135q0 13 9.5 22.5t21.5 9.5h16q14 0 23 -9.5t9 -22.5v-135q0 -12 -9 -21t-23 -9h-16q-12 0 -21.5 9t-9.5 21zM186 101q0 37 21 61t46 42l46 33q21 16 21 38
|
|
||||||
q0 21 -12 30.5t-30 9.5q-12 0 -22 -4.5t-17 -12.5q-4 -4 -7.5 -8t-6.5 -9l-36 24q8 14 22 28q11 12 28.5 20.5t42.5 8.5q37 0 64.5 -22t27.5 -62q0 -22 -9.5 -38t-24.5 -29.5t-32 -24.5l-31 -20q-15 -11 -25 -22.5t-10 -26.5h97v36h44v-77h-195q-1 7 -1.5 13t-0.5 12z
|
|
||||||
M356 557v135q0 13 9 22.5t21 9.5h17q13 0 22.5 -9.5t9.5 -22.5v-135q0 -12 -9.5 -21t-22.5 -9h-17q-12 0 -21 9t-9 21zM418 282v74h197v-35l-123 -245h-54l113 223q3 9 6 12l3 4v1q-3 0 -5 -1h-14h-79v-33h-44zM575 557v135q0 13 9 22.5t22 9.5h16q13 0 22.5 -9.5t9.5 -22.5
|
|
||||||
v-135q0 -12 -9.5 -21t-22.5 -9h-16q-13 0 -22 9t-9 21z" />
|
|
||||||
<glyph glyph-name="u1F4C8" unicode="📈" horiz-adv-x="1228"
|
|
||||||
d="M205 -64v666h102v-374l251 358l220 -276l113 77l61 -82l-194 -134l-195 246l-256 -363v-16h666v-102h-768z" />
|
|
||||||
<glyph glyph-name="u1F4CA" unicode="📊" horiz-adv-x="1228"
|
|
||||||
d="M256 -64v307h205v-307h-205zM512 -59v712h205v-712h-205zM768 -59v507h205v-507h-205z" />
|
|
||||||
<glyph glyph-name="u1F4CC" unicode="📌"
|
|
||||||
d="M1 419q4 40 22 85t49.5 87t69 72t75.5 45.5t72 15t59 -19.5q27 -19 37 -52.5t6 -74.5l148 -110q55 30 107 35t89 -23q26 -20 39 -53t12 -74t-15 -88t-42 -93l212 -203q5 -5 6 -12.5t-4 -12.5q-5 -9 -16 -9q-3 0 -9 2l-255 146q-37 -39 -77.5 -66.5t-79.5 -40t-74.5 -10
|
|
||||||
t-63.5 23.5q-36 27 -46.5 78t3.5 112l-148 112q-38 -16 -73 -16t-61 19q-25 19 -35.5 52t-6.5 73zM84 374q1 -8 9 -13q8 -8 23 -8q16 0 33 8.5t35 23t35.5 33t33.5 39.5q5 7 4 14.5t-9 13.5q-6 5 -14 4t-13 -8q-39 -52 -69 -71.5t-36 -17.5q-7 5 -14.5 4t-13.5 -8
|
|
||||||
q-5 -6 -4 -14zM231 322l178 -134q7 -4 12 -4q11 0 16 8q5 6 4 14t-7 13l-172 128q-16 -15 -31 -25zM414 83q-11 -17 4 -29q14 -10 35 -10q20 0 43 10.5t46.5 28.5t47 41t43.5 49q5 8 4 15.5t-9 13.5q-6 5 -14 3.5t-13 -8.5q-25 -33 -50.5 -57t-47.5 -37.5t-38.5 -17.5
|
|
||||||
t-23.5 2q-6 5 -14 3.5t-13 -7.5z" />
|
|
||||||
<glyph glyph-name="u1F4CE" unicode="📎" horiz-adv-x="965"
|
|
||||||
d="M259 -192q-58 0 -110 26.5t-84.5 72t-45.5 100t6.5 116t72.5 114.5l222 222l273 274q37 37 83.5 51t92.5 2q45 -12 79 -46t46 -79q12 -46 -2 -92.5t-51 -83.5l-474 -473q-24 -24 -52 -37t-60 -10t-56 27q-19 19 -25.5 46t4.5 60t40 62l333 332q11 10 25 10t24 -10t10 -24
|
|
||||||
t-10 -25l-332 -333q-21 -21 -25.5 -40.5t5.5 -27.5q9 -9 23 -7q25 3 47 26l473 474q50 52 35 108q-8 27 -28 47t-47 28q-57 16 -108 -35l-274 -273l-222 -222q-44 -44 -56.5 -94t1 -91t45.5 -73t73 -45.5t91 -1t94 56.5l495 496q10 10 25 10t25 -10t10 -25t-10 -25
|
|
||||||
l-496 -495q-83 -83 -185 -83z" />
|
|
||||||
<glyph glyph-name="u1F4D6" unicode="📖" horiz-adv-x="930"
|
|
||||||
d="M355 186v-67l-200 80v67zM355 394v-68l-200 80v68zM846 746q26 10 47.5 -6t21.5 -40v-640q0 -33 -31 -46l-400 -160q-2 0 -5.5 -1.5t-7 -2t-6.5 -0.5t-6.5 0.5t-7 2t-5.5 1.5l-400 160q-31 13 -31 46v640q0 24 21.5 40t47.5 6l381 -152zM415 -48v561l-320 128v-561z
|
|
||||||
M835 80v561l-320 -128v-561zM775 266v-67l-200 -80v67zM775 474v-68l-200 -80v68z" />
|
|
||||||
<glyph glyph-name="u1F50D" unicode="🔍" horiz-adv-x="820"
|
|
||||||
d="M787 27q34 -34 7 -61l-47 -47q-14 -14 -33.5 -14t-33.5 14l-190 190q-72 -42 -156 -42q-128 0 -223.5 95.5t-95.5 223.5t90.5 218.5t218.5 90.5t223.5 -95.5t95.5 -223.5q0 -84 -45 -160zM110 386q0 -88 68 -156t156 -68t151 63t63 151t-68 156t-156 68t-151 -63
|
|
||||||
t-63 -151z" />
|
|
||||||
<glyph glyph-name="u1F512" unicode="🔒" horiz-adv-x="730"
|
|
||||||
d="M655 425q21 0 40.5 -19.5t19.5 -40.5v-390q0 -21 -14 -40t-34 -25l-59 -20q-49 -15 -98 -15h-290q-49 0 -98 15l-59 20q-20 6 -34 25t-14 40v390q0 22 15 41t35 19h100v70q0 110 51 170t149 60t149 -60t51 -170v-70h90zM265 515v-90h200v90q0 53 -27 81.5t-73 28.5
|
|
||||||
t-73 -28.5t-27 -81.5z" />
|
|
||||||
<glyph glyph-name="u1F513" unicode="🔓" horiz-adv-x="730"
|
|
||||||
d="M655 400q21 0 40.5 -19.5t19.5 -40.5v-390q0 -21 -14 -40t-34 -25l-59 -20q-49 -15 -98 -15h-290q-49 0 -98 15l-59 20q-20 6 -34 25t-14 40v390q0 22 15 41t35 19h400v140q0 53 -27 81.5t-73 28.5t-73 -28.5t-27 -81.5v-40h-100v20q0 110 51 170t149 60t149 -60t51 -170
|
|
||||||
v-120h90z" />
|
|
||||||
<glyph glyph-name="u1F516" unicode="🔖" horiz-adv-x="631"
|
|
||||||
d="M0 -9v675q0 18 9.5 32t26.5 21q5 3 10.5 4t11.5 1h516q5 0 11 -1t11 -4q17 -7 26.5 -21t9.5 -32v-675q0 -18 -9.5 -32t-26.5 -21q-16 -7 -33.5 -3.5t-28.5 16.5l-218 218l-218 -218q-12 -13 -29 -16.5t-33 3.5q-17 8 -26.5 21.5t-9.5 31.5z" />
|
|
||||||
<glyph glyph-name="u1F517" unicode="🔗"
|
|
||||||
d="M0 92v474q0 32 12.5 61t33.5 50t50 34t62 13h299q-1 -8 -2 -14.5t-1 -15.5v-45q0 -12 3 -24h-299q-24 0 -41.5 -17.5t-17.5 -41.5v-474q0 -24 17.5 -41.5t41.5 -17.5h553q24 0 41 17.5t17 41.5v175q22 -16 47 -25.5t52 -10.5v-139q0 -33 -12.5 -61t-34 -50t-50 -34.5
|
|
||||||
t-60.5 -12.5h-553q-33 0 -62 12.5t-50 34.5t-33.5 50t-12.5 61zM356 202.5q0 12.5 8 21.5l396 397h-138q-13 0 -21 8t-8 21v44q-1 12 7.5 21t21.5 9h296q11 0 20 -9t9 -21v-44v-252q0 -12 -9 -21t-20 -8h-44q-13 0 -21.5 8.5t-8.5 20.5v138l-397 -395q-8 -9 -20.5 -9
|
|
||||||
t-21.5 9l-41 41q-8 8 -8 20.5z" />
|
|
||||||
<glyph glyph-name="u1F525" unicode="🔥" horiz-adv-x="710"
|
|
||||||
d="M0 159q0 62 32 126.5t87 117.5q-12 -79 0.5 -127t32.5 -75q23 -32 55 -47q-25 111 -14 215q4 44 16 92t35.5 95t61.5 90t94 78q-24 -52 -23 -95t11 -75q12 -37 36 -67l32 -39q16 -20 27 -45.5t17 -59.5t6 -82q-9 21 -28 33.5t-43 12.5q-34 0 -56.5 -23t-22.5 -56
|
|
||||||
q0 -17 5.5 -31.5t18 -26.5t32 -18t46.5 -6q46 4 81 32q14 13 27 31.5t21.5 45.5t10.5 63t-5 85h-1q54 -53 86.5 -117.5t32.5 -126.5q0 -56 -28 -98.5t-76.5 -70t-113 -42t-138 -14.5t-138 14.5t-113 42t-76.5 70t-28 98.5z" />
|
|
||||||
<glyph glyph-name="u1F554" unicode="🕔" horiz-adv-x="950"
|
|
||||||
d="M475 760q125 0 231 -61.5t167.5 -167.5t61.5 -231t-61.5 -231t-167.5 -167.5t-231 -61.5t-231 61.5t-167.5 167.5t-61.5 231t61.5 231t167.5 167.5t231 61.5zM475 -60q149 0 254.5 105.5t105.5 254.5t-105.5 254.5t-254.5 105.5t-254.5 -105.5t-105.5 -254.5
|
|
||||||
t105.5 -254.5t254.5 -105.5zM510 560v-246l150 -149l-50 -50l-170 170v275h70z" />
|
|
||||||
<glyph glyph-name="u1F6AB" unicode="🚫" horiz-adv-x="950"
|
|
||||||
d="M475 760q125 0 231 -61.5t167.5 -167.5t61.5 -231t-61.5 -231t-167.5 -167.5t-231 -61.5t-231 61.5t-167.5 167.5t-61.5 231t61.5 231t167.5 167.5t231 61.5zM723 547h-1h1zM125 300q0 -125 79 -222l492 493q-97 79 -221 79q-145 0 -247.5 -102.5t-102.5 -247.5zM227 53
|
|
||||||
l1 -1zM475 -50q145 0 247.5 102.5t102.5 247.5q0 124 -79 221l-492 -492q97 -79 221 -79z" />
|
|
||||||
</font>
|
|
||||||
</defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 63 KiB |
@@ -1,399 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<metadata></metadata>
|
|
||||||
<defs>
|
|
||||||
<font id="fontawesomeregular" horiz-adv-x="1536" >
|
|
||||||
<font-face units-per-em="1792" ascent="1536" descent="-256" />
|
|
||||||
<missing-glyph horiz-adv-x="448" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="448" />
|
|
||||||
<glyph unicode="	" horiz-adv-x="448" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="448" />
|
|
||||||
<glyph unicode="¨" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="©" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="®" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="´" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="Æ" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="768" />
|
|
||||||
<glyph unicode=" " />
|
|
||||||
<glyph unicode=" " horiz-adv-x="768" />
|
|
||||||
<glyph unicode=" " />
|
|
||||||
<glyph unicode=" " horiz-adv-x="512" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="384" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="256" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="256" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="192" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="307" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="85" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="307" />
|
|
||||||
<glyph unicode=" " horiz-adv-x="384" />
|
|
||||||
<glyph unicode="™" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="∞" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="≠" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="" horiz-adv-x="500" d="M0 0z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1699 1350q0 -35 -43 -78l-632 -632v-768h320q26 0 45 -19t19 -45t-19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45t45 19h320v768l-632 632q-43 43 -43 78q0 23 18 36.5t38 17.5t43 4h1408q23 0 43 -4t38 -17.5t18 -36.5z" />
|
|
||||||
<glyph unicode="" d="M1536 1312v-1120q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v537l-768 -237v-709q0 -50 -34 -89t-86 -60.5t-103.5 -32t-96.5 -10.5t-96.5 10.5t-103.5 32t-86 60.5t-34 89 t34 89t86 60.5t103.5 32t96.5 10.5q105 0 192 -39v967q0 31 19 56.5t49 35.5l832 256q12 4 28 4q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -52 -38 -90t-90 -38q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5 t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 32v768q-32 -36 -69 -66q-268 -206 -426 -338q-51 -43 -83 -67t-86.5 -48.5t-102.5 -24.5h-1h-1q-48 0 -102.5 24.5t-86.5 48.5t-83 67q-158 132 -426 338q-37 30 -69 66v-768q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1664 1083v11v13.5t-0.5 13 t-3 12.5t-5.5 9t-9 7.5t-14 2.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5q0 -168 147 -284q193 -152 401 -317q6 -5 35 -29.5t46 -37.5t44.5 -31.5t50.5 -27.5t43 -9h1h1q20 0 43 9t50.5 27.5t44.5 31.5t46 37.5t35 29.5q208 165 401 317q54 43 100.5 115.5t46.5 131.5z M1792 1120v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M896 -128q-26 0 -44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5q224 0 351 -124t127 -344q0 -221 -229 -450l-623 -600 q-18 -18 -44 -18z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -21 -10.5 -35.5t-30.5 -14.5q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455 l502 -73q56 -9 56 -46z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889q0 -22 -26 -48l-363 -354l86 -500q1 -7 1 -20q0 -50 -41 -50q-19 0 -40 12l-449 236l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500 l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41t49 -41l225 -455l502 -73q56 -9 56 -46z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 131q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5t43 97.5t62 81t85.5 53.5t111.5 20q9 0 42 -21.5t74.5 -48t108 -48t133.5 -21.5t133.5 21.5t108 48t74.5 48t42 21.5q61 0 111.5 -20t85.5 -53.5t62 -81 t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M384 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 320v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM384 704v128q0 26 -19 45t-45 19h-128 q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 -64v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM384 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45 t45 -19h128q26 0 45 19t19 45zM1792 -64v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1408 704v512q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-512q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1792 320v128 q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 704v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1792 1088v128q0 26 -19 45t-45 19h-128q-26 0 -45 -19 t-19 -45v-128q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1920 1248v-1344q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1344q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M768 512v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM768 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 512v-384q0 -52 -38 -90t-90 -38 h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90zM1664 1280v-384q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v384q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 288v-192q0 -40 -28 -68t-68 -28h-320 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1152 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M512 288v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM512 800v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 288v-192q0 -40 -28 -68t-68 -28h-960 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68zM512 1312v-192q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h320q40 0 68 -28t28 -68zM1792 800v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28 h960q40 0 68 -28t28 -68zM1792 1312v-192q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h960q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1671 970q0 -40 -28 -68l-724 -724l-136 -136q-28 -28 -68 -28t-68 28l-136 136l-362 362q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -295l656 657q28 28 68 28t68 -28l136 -136q28 -28 28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1298 214q0 -40 -28 -68l-136 -136q-28 -28 -68 -28t-68 28l-294 294l-294 -294q-28 -28 -68 -28t-68 28l-136 136q-28 28 -28 68t28 68l294 294l-294 294q-28 28 -28 68t28 68l136 136q28 28 68 28t68 -28l294 -294l294 294q28 28 68 28t68 -28l136 -136q28 -28 28 -68 t-28 -68l-294 -294l294 -294q28 -28 28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-224q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v224h-224q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h224v224q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5v-224h224 q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5zM1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5 t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1024 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-576q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h576q13 0 22.5 -9.5t9.5 -22.5zM1152 704q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5t316.5 131.5t131.5 316.5z M1664 -128q0 -53 -37.5 -90.5t-90.5 -37.5q-54 0 -90 38l-343 342q-179 -124 -399 -124q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -220 -124 -399l343 -343q37 -37 37 -90z " />
|
|
||||||
<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61t-298 61t-245 164t-164 245t-61 298q0 182 80.5 343t226.5 270q43 32 95.5 25t83.5 -50q32 -42 24.5 -94.5t-49.5 -84.5q-98 -74 -151.5 -181t-53.5 -228q0 -104 40.5 -198.5t109.5 -163.5t163.5 -109.5 t198.5 -40.5t198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5q0 121 -53.5 228t-151.5 181q-42 32 -49.5 84.5t24.5 94.5q31 43 84 50t95 -25q146 -109 226.5 -270t80.5 -343zM896 1408v-640q0 -52 -38 -90t-90 -38t-90 38t-38 90v640q0 52 38 90t90 38t90 -38t38 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M256 96v-192q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM640 224v-320q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1024 480v-576q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23 v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1408 864v-960q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v960q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1792 1376v-1472q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1472q0 14 9 23t23 9h192q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" d="M1024 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1536 749v-222q0 -12 -8 -23t-20 -13l-185 -28q-19 -54 -39 -91q35 -50 107 -138q10 -12 10 -25t-9 -23q-27 -37 -99 -108t-94 -71q-12 0 -26 9l-138 108q-44 -23 -91 -38 q-16 -136 -29 -186q-7 -28 -36 -28h-222q-14 0 -24.5 8.5t-11.5 21.5l-28 184q-49 16 -90 37l-141 -107q-10 -9 -25 -9q-14 0 -25 11q-126 114 -165 168q-7 10 -7 23q0 12 8 23q15 21 51 66.5t54 70.5q-27 50 -41 99l-183 27q-13 2 -21 12.5t-8 23.5v222q0 12 8 23t19 13 l186 28q14 46 39 92q-40 57 -107 138q-10 12 -10 24q0 10 9 23q26 36 98.5 107.5t94.5 71.5q13 0 26 -10l138 -107q44 23 91 38q16 136 29 186q7 28 36 28h222q14 0 24.5 -8.5t11.5 -21.5l28 -184q49 -16 90 -37l142 107q9 9 24 9q13 0 25 -10q129 -119 165 -170q7 -8 7 -22 q0 -12 -8 -23q-15 -21 -51 -66.5t-54 -70.5q26 -50 41 -98l183 -28q13 -2 21 -12.5t8 -23.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M512 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM768 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1024 800v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1152 76v948h-896v-948q0 -22 7 -40.5t14.5 -27t10.5 -8.5h832q3 0 10.5 8.5t14.5 27t7 40.5zM480 1152h448l-48 117q-7 9 -17 11h-317q-10 -2 -17 -11zM1408 1120v-64q0 -14 -9 -23t-23 -9h-96v-948q0 -83 -47 -143.5t-113 -60.5h-832 q-66 0 -113 58.5t-47 141.5v952h-96q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h309l70 167q15 37 54 63t79 26h320q40 0 79 -26t54 -63l70 -167h309q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 544v-480q0 -26 -19 -45t-45 -19h-384v384h-256v-384h-384q-26 0 -45 19t-19 45v480q0 1 0.5 3t0.5 3l575 474l575 -474q1 -2 1 -6zM1631 613l-62 -74q-8 -9 -21 -11h-3q-13 0 -21 7l-692 577l-692 -577q-12 -8 -24 -7q-13 2 -21 11l-62 74q-8 10 -7 23.5t11 21.5 l719 599q32 26 76 26t76 -26l244 -204v195q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-408l219 -182q10 -8 11 -21.5t-7 -23.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280zM768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z " />
|
|
||||||
<glyph unicode="" d="M896 992v-448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v352q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1111 540v4l-24 320q-1 13 -11 22.5t-23 9.5h-186q-13 0 -23 -9.5t-11 -22.5l-24 -320v-4q-1 -12 8 -20t21 -8h244q12 0 21 8t8 20zM1870 73q0 -73 -46 -73h-704q13 0 22 9.5t8 22.5l-20 256q-1 13 -11 22.5t-23 9.5h-272q-13 0 -23 -9.5t-11 -22.5l-20 -256 q-1 -13 8 -22.5t22 -9.5h-704q-46 0 -46 73q0 54 26 116l417 1044q8 19 26 33t38 14h339q-13 0 -23 -9.5t-11 -22.5l-15 -192q-1 -14 8 -23t22 -9h166q13 0 22 9t8 23l-15 192q-1 13 -11 22.5t-23 9.5h339q20 0 38 -14t26 -33l417 -1044q26 -62 26 -116z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 416v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h465l135 -136 q58 -56 136 -56t136 56l136 136h464q40 0 68 -28t28 -68zM1339 985q17 -41 -14 -70l-448 -448q-18 -19 -45 -19t-45 19l-448 448q-31 29 -14 70q17 39 59 39h256v448q0 26 19 45t45 19h256q26 0 45 -19t19 -45v-448h256q42 0 59 -39z" />
|
|
||||||
<glyph unicode="" d="M1120 608q0 -12 -10 -24l-319 -319q-11 -9 -23 -9t-23 9l-320 320q-15 16 -7 35q8 20 30 20h192v352q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-352h192q14 0 23 -9t9 -23zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273 t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1118 660q-8 -20 -30 -20h-192v-352q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v352h-192q-14 0 -23 9t-9 23q0 12 10 24l319 319q11 9 23 9t23 -9l320 -320q15 -16 7 -35zM768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198 t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1023 576h316q-1 3 -2.5 8t-2.5 8l-212 496h-708l-212 -496q-1 -2 -2.5 -8t-2.5 -8h316l95 -192h320zM1536 546v-482q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v482q0 62 25 123l238 552q10 25 36.5 42t52.5 17h832q26 0 52.5 -17t36.5 -42l238 -552 q25 -61 25 -123z" />
|
|
||||||
<glyph unicode="" d="M1184 640q0 -37 -32 -55l-544 -320q-15 -9 -32 -9q-16 0 -32 8q-32 19 -32 56v640q0 37 32 56q33 18 64 -1l544 -320q32 -18 32 -55zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l138 138q-148 137 -349 137q-104 0 -198.5 -40.5t-163.5 -109.5t-109.5 -163.5t-40.5 -198.5t40.5 -198.5t109.5 -163.5t163.5 -109.5t198.5 -40.5q119 0 225 52t179 147q7 10 23 12q14 0 25 -9 l137 -138q9 -8 9.5 -20.5t-7.5 -22.5q-109 -132 -264 -204.5t-327 -72.5q-156 0 -298 61t-245 164t-164 245t-61 298t61 298t164 245t245 164t298 61q147 0 284.5 -55.5t244.5 -156.5l130 129q29 31 70 14q39 -17 39 -59z" />
|
|
||||||
<glyph unicode="" d="M1511 480q0 -5 -1 -7q-64 -268 -268 -434.5t-478 -166.5q-146 0 -282.5 55t-243.5 157l-129 -129q-19 -19 -45 -19t-45 19t-19 45v448q0 26 19 45t45 19h448q26 0 45 -19t19 -45t-19 -45l-137 -137q71 -66 161 -102t187 -36q134 0 250 65t186 179q11 17 53 117 q8 23 30 23h192q13 0 22.5 -9.5t9.5 -22.5zM1536 1280v-448q0 -26 -19 -45t-45 -19h-448q-26 0 -45 19t-19 45t19 45l138 138q-148 137 -349 137q-134 0 -250 -65t-186 -179q-11 -17 -53 -117q-8 -23 -30 -23h-199q-13 0 -22.5 9.5t-9.5 22.5v7q65 268 270 434.5t480 166.5 q146 0 284 -55.5t245 -156.5l130 129q19 19 45 19t45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M384 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M384 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1536 352v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5z M1536 608v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5t9.5 -22.5zM1536 864v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h960q13 0 22.5 -9.5 t9.5 -22.5zM1664 160v832q0 13 -9.5 22.5t-22.5 9.5h-1472q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1472q13 0 22.5 9.5t9.5 22.5zM1792 1248v-1088q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1472q66 0 113 -47 t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M320 768h512v192q0 106 -75 181t-181 75t-181 -75t-75 -181v-192zM1152 672v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v192q0 184 132 316t316 132t316 -132t132 -316v-192h32q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M320 1280q0 -72 -64 -110v-1266q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v1266q-64 38 -64 110q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -25 -12.5 -38.5t-39.5 -27.5q-215 -116 -369 -116q-61 0 -123.5 22t-108.5 48 t-115.5 48t-142.5 22q-192 0 -464 -146q-17 -9 -33 -9q-26 0 -45 19t-19 45v742q0 32 31 55q21 14 79 43q236 120 421 120q107 0 200 -29t219 -88q38 -19 88 -19q54 0 117.5 21t110 47t88 47t54.5 21q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 650q0 -166 -60 -314l-20 -49l-185 -33q-22 -83 -90.5 -136.5t-156.5 -53.5v-32q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-32q71 0 130 -35.5t93 -95.5l68 12q29 95 29 193q0 148 -88 279t-236.5 209t-315.5 78 t-315.5 -78t-236.5 -209t-88 -279q0 -98 29 -193l68 -12q34 60 93 95.5t130 35.5v32q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v32q-88 0 -156.5 53.5t-90.5 136.5l-185 33l-20 49q-60 148 -60 314q0 151 67 291t179 242.5 t266 163.5t320 61t320 -61t266 -163.5t179 -242.5t67 -291z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M768 1184v-1088q0 -26 -19 -45t-45 -19t-45 19l-333 333h-262q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h262l333 333q19 19 45 19t45 -19t19 -45zM1152 640q0 -76 -42.5 -141.5t-112.5 -93.5q-10 -5 -25 -5q-26 0 -45 18.5t-19 45.5q0 21 12 35.5t29 25t34 23t29 35.5 t12 57t-12 57t-29 35.5t-34 23t-29 25t-12 35.5q0 27 19 45.5t45 18.5q15 0 25 -5q70 -27 112.5 -93t42.5 -142zM1408 640q0 -153 -85 -282.5t-225 -188.5q-13 -5 -25 -5q-27 0 -46 19t-19 45q0 39 39 59q56 29 76 44q74 54 115.5 135.5t41.5 173.5t-41.5 173.5 t-115.5 135.5q-20 15 -76 44q-39 20 -39 59q0 26 19 45t45 19q13 0 26 -5q140 -59 225 -188.5t85 -282.5zM1664 640q0 -230 -127 -422.5t-338 -283.5q-13 -5 -26 -5q-26 0 -45 19t-19 45q0 36 39 59q7 4 22.5 10.5t22.5 10.5q46 25 82 51q123 91 192 227t69 289t-69 289 t-192 227q-36 26 -82 51q-7 4 -22.5 10.5t-22.5 10.5q-39 23 -39 59q0 26 19 45t45 19q13 0 26 -5q211 -91 338 -283.5t127 -422.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 384v-128h-128v128h128zM384 1152v-128h-128v128h128zM1152 1152v-128h-128v128h128zM128 129h384v383h-384v-383zM128 896h384v384h-384v-384zM896 896h384v384h-384v-384zM640 640v-640h-640v640h640zM1152 128v-128h-128v128h128zM1408 128v-128h-128v128h128z M1408 640v-384h-384v128h-128v-384h-128v640h384v-128h128v128h128zM640 1408v-640h-640v640h640zM1408 1408v-640h-640v640h640z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M63 0h-63v1408h63v-1408zM126 1h-32v1407h32v-1407zM220 1h-31v1407h31v-1407zM377 1h-31v1407h31v-1407zM534 1h-62v1407h62v-1407zM660 1h-31v1407h31v-1407zM723 1h-31v1407h31v-1407zM786 1h-31v1407h31v-1407zM943 1h-63v1407h63v-1407zM1100 1h-63v1407h63v-1407z M1226 1h-63v1407h63v-1407zM1352 1h-63v1407h63v-1407zM1446 1h-63v1407h63v-1407zM1635 1h-94v1407h94v-1407zM1698 1h-32v1407h32v-1407zM1792 0h-63v1408h63v-1408z" />
|
|
||||||
<glyph unicode="" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M448 1088q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1515 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-53 0 -90 37l-715 716q-38 37 -64.5 101t-26.5 117v416q0 52 38 90t90 38h416q53 0 117 -26.5t102 -64.5 l715 -714q37 -39 37 -91zM1899 512q0 -53 -37 -90l-491 -492q-39 -37 -91 -37q-36 0 -59 14t-53 45l470 470q37 37 37 90q0 52 -37 91l-715 714q-38 38 -102 64.5t-117 26.5h224q53 0 117 -26.5t102 -64.5l715 -714q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1639 1058q40 -57 18 -129l-275 -906q-19 -64 -76.5 -107.5t-122.5 -43.5h-923q-77 0 -148.5 53.5t-99.5 131.5q-24 67 -2 127q0 4 3 27t4 37q1 8 -3 21.5t-3 19.5q2 11 8 21t16.5 23.5t16.5 23.5q23 38 45 91.5t30 91.5q3 10 0.5 30t-0.5 28q3 11 17 28t17 23 q21 36 42 92t25 90q1 9 -2.5 32t0.5 28q4 13 22 30.5t22 22.5q19 26 42.5 84.5t27.5 96.5q1 8 -3 25.5t-2 26.5q2 8 9 18t18 23t17 21q8 12 16.5 30.5t15 35t16 36t19.5 32t26.5 23.5t36 11.5t47.5 -5.5l-1 -3q38 9 51 9h761q74 0 114 -56t18 -130l-274 -906 q-36 -119 -71.5 -153.5t-128.5 -34.5h-869q-27 0 -38 -15q-11 -16 -1 -43q24 -70 144 -70h923q29 0 56 15.5t35 41.5l300 987q7 22 5 57q38 -15 59 -43zM575 1056q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5 t-16.5 -22.5zM492 800q-4 -13 2 -22.5t20 -9.5h608q13 0 25.5 9.5t16.5 22.5l21 64q4 13 -2 22.5t-20 9.5h-608q-13 0 -25.5 -9.5t-16.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M384 0h896v256h-896v-256zM384 640h896v384h-160q-40 0 -68 28t-28 68v160h-640v-640zM1536 576q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 576v-416q0 -13 -9.5 -22.5t-22.5 -9.5h-224v-160q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68 v160h-224q-13 0 -22.5 9.5t-9.5 22.5v416q0 79 56.5 135.5t135.5 56.5h64v544q0 40 28 68t68 28h672q40 0 88 -20t76 -48l152 -152q28 -28 48 -76t20 -88v-256h64q79 0 135.5 -56.5t56.5 -135.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M960 864q119 0 203.5 -84.5t84.5 -203.5t-84.5 -203.5t-203.5 -84.5t-203.5 84.5t-84.5 203.5t84.5 203.5t203.5 84.5zM1664 1280q106 0 181 -75t75 -181v-896q0 -106 -75 -181t-181 -75h-1408q-106 0 -181 75t-75 181v896q0 106 75 181t181 75h224l51 136 q19 49 69.5 84.5t103.5 35.5h512q53 0 103.5 -35.5t69.5 -84.5l51 -136h224zM960 128q185 0 316.5 131.5t131.5 316.5t-131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5t131.5 -316.5t316.5 -131.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M725 977l-170 -450q73 -1 153.5 -2t119 -1.5t52.5 -0.5l29 2q-32 95 -92 241q-53 132 -92 211zM21 -128h-21l2 79q22 7 80 18q89 16 110 31q20 16 48 68l237 616l280 724h75h53l11 -21l205 -480q103 -242 124 -297q39 -102 96 -235q26 -58 65 -164q24 -67 65 -149 q22 -49 35 -57q22 -19 69 -23q47 -6 103 -27q6 -39 6 -57q0 -14 -1 -26q-80 0 -192 8q-93 8 -189 8q-79 0 -135 -2l-200 -11l-58 -2q0 45 4 78l131 28q56 13 68 23q12 12 12 27t-6 32l-47 114l-92 228l-450 2q-29 -65 -104 -274q-23 -64 -23 -84q0 -31 17 -43 q26 -21 103 -32q3 0 13.5 -2t30 -5t40.5 -6q1 -28 1 -58q0 -17 -2 -27q-66 0 -349 20l-48 -8q-81 -14 -167 -14z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M555 15q76 -32 140 -32q131 0 216 41t122 113q38 70 38 181q0 114 -41 180q-58 94 -141 126q-80 32 -247 32q-74 0 -101 -10v-144l-1 -173l3 -270q0 -15 12 -44zM541 761q43 -7 109 -7q175 0 264 65t89 224q0 112 -85 187q-84 75 -255 75q-52 0 -130 -13q0 -44 2 -77 q7 -122 6 -279l-1 -98q0 -43 1 -77zM0 -128l2 94q45 9 68 12q77 12 123 31q17 27 21 51q9 66 9 194l-2 497q-5 256 -9 404q-1 87 -11 109q-1 4 -12 12q-18 12 -69 15q-30 2 -114 13l-4 83l260 6l380 13l45 1q5 0 14 0.5t14 0.5q1 0 21.5 -0.5t40.5 -0.5h74q88 0 191 -27 q43 -13 96 -39q57 -29 102 -76q44 -47 65 -104t21 -122q0 -70 -32 -128t-95 -105q-26 -20 -150 -77q177 -41 267 -146q92 -106 92 -236q0 -76 -29 -161q-21 -62 -71 -117q-66 -72 -140 -108q-73 -36 -203 -60q-82 -15 -198 -11l-197 4q-84 2 -298 -11q-33 -3 -272 -11z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M0 -126l17 85q4 1 77 20q76 19 116 39q29 37 41 101l27 139l56 268l12 64q8 44 17 84.5t16 67t12.5 46.5t9 30.5t3.5 11.5l29 157l16 63l22 135l8 50v38q-41 22 -144 28q-28 2 -38 4l19 103l317 -14q39 -2 73 -2q66 0 214 9q33 2 68 4.5t36 2.5q-2 -19 -6 -38 q-7 -29 -13 -51q-55 -19 -109 -31q-64 -16 -101 -31q-12 -31 -24 -88q-9 -44 -13 -82q-44 -199 -66 -306l-61 -311l-38 -158l-43 -235l-12 -45q-2 -7 1 -27q64 -15 119 -21q36 -5 66 -10q-1 -29 -7 -58q-7 -31 -9 -41q-18 0 -23 -1q-24 -2 -42 -2q-9 0 -28 3q-19 4 -145 17 l-198 2q-41 1 -174 -11q-74 -7 -98 -9z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l215 -1h293l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -42.5 2t-103.5 -1t-111 -1 q-34 0 -67 -5q-10 -97 -8 -136l1 -152v-332l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-88 0 -233 -14q-48 -4 -70 -4q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q8 192 6 433l-5 428q-1 62 -0.5 118.5t0.5 102.5t-2 57t-6 15q-6 5 -14 6q-38 6 -148 6q-43 0 -100 -13.5t-73 -24.5q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1744 128q33 0 42 -18.5t-11 -44.5 l-126 -162q-20 -26 -49 -26t-49 26l-126 162q-20 26 -11 44.5t42 18.5h80v1024h-80q-33 0 -42 18.5t11 44.5l126 162q20 26 49 26t49 -26l126 -162q20 -26 11 -44.5t-42 -18.5h-80v-1024h80z" />
|
|
||||||
<glyph unicode="" d="M81 1407l54 -27q20 -5 211 -5h130l19 3l115 1l446 -1h318l34 -2q14 -1 28 7t21 16l7 8l42 1q15 0 28 -1v-104.5t1 -131.5l1 -100l-1 -58q0 -32 -4 -51q-39 -15 -68 -18q-25 43 -54 128q-8 24 -15.5 62.5t-11.5 65.5t-6 29q-13 15 -27 19q-7 2 -58.5 2t-138.5 -1t-128 -1 q-94 0 -127 -5q-10 -97 -8 -136l1 -152v52l3 -359l-1 -147q-1 -46 11 -85q49 -25 89 -32q2 0 18 -5t44 -13t43 -12q30 -8 50 -18q5 -45 5 -50q0 -10 -3 -29q-14 -1 -34 -1q-110 0 -187 10q-72 8 -238 8q-82 0 -233 -13q-45 -5 -70 -5q-2 22 -2 26l-1 26v9q21 33 79 49 q139 38 159 50q9 21 12 56q6 137 6 433l-5 44q0 265 -2 278q-2 11 -6 15q-6 5 -14 6q-38 6 -148 6q-50 0 -168.5 -14t-132.5 -24q-13 -9 -22 -33t-22 -75t-24 -84q-6 -19 -19.5 -32t-20.5 -13q-44 27 -56 44v297v86zM1505 113q26 -20 26 -49t-26 -49l-162 -126 q-26 -20 -44.5 -11t-18.5 42v80h-1024v-80q0 -33 -18.5 -42t-44.5 11l-162 126q-26 20 -26 49t26 49l162 126q26 20 44.5 11t18.5 -42v-80h1024v80q0 33 18.5 42t44.5 -11z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1408 576v-128q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h896q26 0 45 -19t19 -45zM1664 960v-128q0 -26 -19 -45t-45 -19 h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1280 1344v-128q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h640q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1280q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1536q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1536q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1152q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 192v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 576v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 960v-128q0 -26 -19 -45 t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-128q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M256 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM256 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5 t9.5 -22.5zM256 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344 q13 0 22.5 -9.5t9.5 -22.5zM256 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-192q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h192q13 0 22.5 -9.5t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v192 q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M384 992v-576q0 -13 -9.5 -22.5t-22.5 -9.5q-14 0 -23 9l-288 288q-9 9 -9 23t9 23l288 288q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M352 704q0 -14 -9 -23l-288 -288q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v576q0 13 9.5 22.5t22.5 9.5q14 0 23 -9l288 -288q9 -9 9 -23zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5 t9.5 -22.5zM1792 608v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088q13 0 22.5 -9.5t9.5 -22.5zM1792 992v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1088q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1088 q13 0 22.5 -9.5t9.5 -22.5zM1792 1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1728q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1728q13 0 22.5 -9.5t9.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 1184v-1088q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-403 403v-166q0 -119 -84.5 -203.5t-203.5 -84.5h-704q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h704q119 0 203.5 -84.5t84.5 -203.5v-165l403 402q18 19 45 19q12 0 25 -5 q39 -17 39 -59z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M640 960q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 576v-448h-1408v192l320 320l160 -160l512 512zM1760 1280h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5v1216 q0 13 -9.5 22.5t-22.5 9.5zM1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928q0 22 -22 22q-10 0 -17 -7l-542 -542q-7 -7 -7 -17q0 -22 22 -22q10 0 17 7l542 542q7 7 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024q0 -53 -37 -90l-166 -166l-416 416l166 165q36 38 90 38 q53 0 91 -38l235 -234q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M768 896q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1024 896q0 -109 -33 -179l-364 -774q-16 -33 -47.5 -52t-67.5 -19t-67.5 19t-46.5 52l-365 774q-33 70 -33 179q0 212 150 362t362 150t362 -150t150 -362z" />
|
|
||||||
<glyph unicode="" d="M768 96v1088q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M512 384q0 36 -20 69q-1 1 -15.5 22.5t-25.5 38t-25 44t-21 50.5q-4 16 -21 16t-21 -16q-7 -23 -21 -50.5t-25 -44t-25.5 -38t-15.5 -22.5q-20 -33 -20 -69q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 512q0 -212 -150 -362t-362 -150t-362 150t-150 362 q0 145 81 275q6 9 62.5 90.5t101 151t99.5 178t83 201.5q9 30 34 47t51 17t51.5 -17t33.5 -47q28 -93 83 -201.5t99.5 -178t101 -151t62.5 -90.5q81 -127 81 -275z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M888 352l116 116l-152 152l-116 -116v-56h96v-96h56zM1328 1072q-16 16 -33 -1l-350 -350q-17 -17 -1 -33t33 1l350 350q17 17 1 33zM1408 478v-190q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-14 -14 -32 -8q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v126q0 13 9 22l64 64q15 15 35 7t20 -29zM1312 1216l288 -288l-672 -672h-288v288zM1756 1084l-92 -92 l-288 288l92 92q28 28 68 28t68 -28l152 -152q28 -28 28 -68t-28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 547v-259q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h255v0q13 0 22.5 -9.5t9.5 -22.5q0 -27 -26 -32q-77 -26 -133 -60q-10 -4 -16 -4h-112q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832 q66 0 113 47t47 113v214q0 19 18 29q28 13 54 37q16 16 35 8q21 -9 21 -29zM1645 1043l-384 -384q-18 -19 -45 -19q-12 0 -25 5q-39 17 -39 59v192h-160q-323 0 -438 -131q-119 -137 -74 -473q3 -23 -20 -34q-8 -2 -12 -2q-16 0 -26 13q-10 14 -21 31t-39.5 68.5t-49.5 99.5 t-38.5 114t-17.5 122q0 49 3.5 91t14 90t28 88t47 81.5t68.5 74t94.5 61.5t124.5 48.5t159.5 30.5t196.5 11h160v192q0 42 39 59q13 5 25 5q26 0 45 -19l384 -384q19 -19 19 -45t-19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1408 606v-318q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q63 0 117 -25q15 -7 18 -23q3 -17 -9 -29l-49 -49q-10 -10 -23 -10q-3 0 -9 2q-23 6 -45 6h-832q-66 0 -113 -47t-47 -113v-832 q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v254q0 13 9 22l64 64q10 10 23 10q6 0 12 -3q20 -8 20 -29zM1639 1095l-814 -814q-24 -24 -57 -24t-57 24l-430 430q-24 24 -24 57t24 57l110 110q24 24 57 24t57 -24l263 -263l647 647q24 24 57 24t57 -24l110 -110 q24 -24 24 -57t-24 -57z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-384v-384h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v384h-384v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45 t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h384v384h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45t-19 -45t-45 -19h-128v-384h384v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M979 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1747 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-9 9 -13 19v-678q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-678q4 11 13 19l710 710 q19 19 32 13t13 -32v-710q4 11 13 19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1619 1395q19 19 32 13t13 -32v-1472q0 -26 -13 -32t-32 13l-710 710q-8 9 -13 19v-710q0 -26 -13 -32t-32 13l-710 710q-19 19 -19 45t19 45l710 710q19 19 32 13t13 -32v-710q5 11 13 19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1384 609l-1328 -738q-23 -13 -39.5 -3t-16.5 36v1472q0 26 16.5 36t39.5 -3l1328 -738q23 -13 23 -31t-23 -31z" />
|
|
||||||
<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45zM640 1344v-1408q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h512q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" d="M1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q19 -19 19 -45t-19 -45l-710 -710q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v710q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19l-710 -710 q-19 -19 -32 -13t-13 32v710q-5 -10 -13 -19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M45 -115q-19 -19 -32 -13t-13 32v1472q0 26 13 32t32 -13l710 -710q8 -8 13 -19v678q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-1408q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v678q-5 -10 -13 -19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1538" d="M14 557l710 710q19 19 45 19t45 -19l710 -710q19 -19 13 -32t-32 -13h-1472q-26 0 -32 13t13 32zM1473 0h-1408q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1408q26 0 45 -19t19 -45v-256q0 -26 -19 -45t-45 -19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M742 -37l-652 651q-37 37 -37 90.5t37 90.5l652 651q37 37 90.5 37t90.5 -37l75 -75q37 -37 37 -90.5t-37 -90.5l-486 -486l486 -485q37 -38 37 -91t-37 -90l-75 -75q-37 -37 -90.5 -37t-90.5 37z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1099 704q0 -52 -37 -91l-652 -651q-37 -37 -90 -37t-90 37l-76 75q-37 39 -37 91q0 53 37 90l486 486l-486 485q-37 39 -37 91q0 53 37 90l76 75q36 38 90 38t90 -38l652 -651q37 -37 37 -90z" />
|
|
||||||
<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-256v256q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-256h-256q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h256v-256q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v256h256q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5 t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1216 576v128q0 26 -19 45t-45 19h-768q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h768q26 0 45 19t19 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1149 414q0 26 -19 45l-181 181l181 181q19 19 19 45q0 27 -19 46l-90 90q-19 19 -46 19q-26 0 -45 -19l-181 -181l-181 181q-19 19 -45 19q-27 0 -46 -19l-90 -90q-19 -19 -19 -46q0 -26 19 -45l181 -181l-181 -181q-19 -19 -19 -45q0 -27 19 -46l90 -90q19 -19 46 -19 q26 0 45 19l181 181l181 -181q19 -19 45 -19q27 0 46 19l90 90q19 19 19 46zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1284 802q0 28 -18 46l-91 90q-19 19 -45 19t-45 -19l-408 -407l-226 226q-19 19 -45 19t-45 -19l-91 -90q-18 -18 -18 -46q0 -27 18 -45l362 -362q19 -19 45 -19q27 0 46 19l543 543q18 18 18 45zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M896 160v192q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h192q14 0 23 9t9 23zM1152 832q0 88 -55.5 163t-138.5 116t-170 41q-243 0 -371 -213q-15 -24 8 -42l132 -100q7 -6 19 -6q16 0 25 12q53 68 86 92q34 24 86 24q48 0 85.5 -26t37.5 -59 q0 -38 -20 -61t-68 -45q-63 -28 -115.5 -86.5t-52.5 -125.5v-36q0 -14 9 -23t23 -9h192q14 0 23 9t9 23q0 19 21.5 49.5t54.5 49.5q32 18 49 28.5t46 35t44.5 48t28 60.5t12.5 81zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1024 160v160q0 14 -9 23t-23 9h-96v512q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h96v-320h-96q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23t23 -9h448q14 0 23 9t9 23zM896 1056v160q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-160q0 -14 9 -23 t23 -9h192q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1197 512h-109q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h109q-32 108 -112.5 188.5t-188.5 112.5v-109q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v109q-108 -32 -188.5 -112.5t-112.5 -188.5h109q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-109 q32 -108 112.5 -188.5t188.5 -112.5v109q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-109q108 32 188.5 112.5t112.5 188.5zM1536 704v-128q0 -26 -19 -45t-45 -19h-143q-37 -161 -154.5 -278.5t-278.5 -154.5v-143q0 -26 -19 -45t-45 -19h-128q-26 0 -45 19t-19 45v143 q-161 37 -278.5 154.5t-154.5 278.5h-143q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h143q37 161 154.5 278.5t278.5 154.5v143q0 26 19 45t45 19h128q26 0 45 -19t19 -45v-143q161 -37 278.5 -154.5t154.5 -278.5h143q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" d="M1097 457l-146 -146q-10 -10 -23 -10t-23 10l-137 137l-137 -137q-10 -10 -23 -10t-23 10l-146 146q-10 10 -10 23t10 23l137 137l-137 137q-10 10 -10 23t10 23l146 146q10 10 23 10t23 -10l137 -137l137 137q10 10 23 10t23 -10l146 -146q10 -10 10 -23t-10 -23 l-137 -137l137 -137q10 -10 10 -23t-10 -23zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5 t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1171 723l-422 -422q-19 -19 -45 -19t-45 19l-294 294q-19 19 -19 45t19 45l102 102q19 19 45 19t45 -19l147 -147l275 275q19 19 45 19t45 -19l102 -102q19 -19 19 -45t-19 -45zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198 t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1312 643q0 161 -87 295l-754 -753q137 -89 297 -89q111 0 211.5 43.5t173.5 116.5t116 174.5t43 212.5zM313 344l755 754q-135 91 -300 91q-148 0 -273 -73t-198 -199t-73 -274q0 -162 89 -299zM1536 643q0 -157 -61 -300t-163.5 -246t-245 -164t-298.5 -61t-298.5 61 t-245 164t-163.5 246t-61 300t61 299.5t163.5 245.5t245 164t298.5 61t298.5 -61t245 -164t163.5 -245.5t61 -299.5z" />
|
|
||||||
<glyph unicode="" d="M1536 640v-128q0 -53 -32.5 -90.5t-84.5 -37.5h-704l293 -294q38 -36 38 -90t-38 -90l-75 -76q-37 -37 -90 -37q-52 0 -91 37l-651 652q-37 37 -37 90q0 52 37 91l651 650q38 38 91 38q52 0 90 -38l75 -74q38 -38 38 -91t-38 -91l-293 -293h704q52 0 84.5 -37.5 t32.5 -90.5z" />
|
|
||||||
<glyph unicode="" d="M1472 576q0 -54 -37 -91l-651 -651q-39 -37 -91 -37q-51 0 -90 37l-75 75q-38 38 -38 91t38 91l293 293h-704q-52 0 -84.5 37.5t-32.5 90.5v128q0 53 32.5 90.5t84.5 37.5h704l-293 294q-38 36 -38 90t38 90l75 75q38 38 90 38q53 0 91 -38l651 -651q37 -35 37 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 565q0 -51 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-294 293v-704q0 -52 -37.5 -84.5t-90.5 -32.5h-128q-53 0 -90.5 32.5t-37.5 84.5v704l-294 -293q-36 -38 -90 -38t-90 38l-75 75q-38 38 -38 90q0 53 38 91l651 651q35 37 90 37q54 0 91 -37l651 -651 q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 704q0 -53 -37 -90l-651 -652q-39 -37 -91 -37q-53 0 -90 37l-651 652q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l294 -294v704q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-704l294 294q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 896q0 -26 -19 -45l-512 -512q-19 -19 -45 -19t-45 19t-19 45v256h-224q-98 0 -175.5 -6t-154 -21.5t-133 -42.5t-105.5 -69.5t-80 -101t-48.5 -138.5t-17.5 -181q0 -55 5 -123q0 -6 2.5 -23.5t2.5 -26.5q0 -15 -8.5 -25t-23.5 -10q-16 0 -28 17q-7 9 -13 22 t-13.5 30t-10.5 24q-127 285 -127 451q0 199 53 333q162 403 875 403h224v256q0 26 19 45t45 19t45 -19l512 -512q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" d="M755 480q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23zM1536 1344v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332 q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" d="M768 576v-448q0 -26 -19 -45t-45 -19t-45 19l-144 144l-332 -332q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l332 332l-144 144q-19 19 -19 45t19 45t45 19h448q26 0 45 -19t19 -45zM1523 1248q0 -13 -10 -23l-332 -332l144 -144q19 -19 19 -45t-19 -45 t-45 -19h-448q-26 0 -45 19t-19 45v448q0 26 19 45t45 19t45 -19l144 -144l332 332q10 10 23 10t23 -10l114 -114q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-416v-416q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v416h-416q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h416v416q0 40 28 68t68 28h192q40 0 68 -28t28 -68v-416h416q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 800v-192q0 -40 -28 -68t-68 -28h-1216q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h1216q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1482 486q46 -26 59.5 -77.5t-12.5 -97.5l-64 -110q-26 -46 -77.5 -59.5t-97.5 12.5l-266 153v-307q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v307l-266 -153q-46 -26 -97.5 -12.5t-77.5 59.5l-64 110q-26 46 -12.5 97.5t59.5 77.5l266 154l-266 154 q-46 26 -59.5 77.5t12.5 97.5l64 110q26 46 77.5 59.5t97.5 -12.5l266 -153v307q0 52 38 90t90 38h128q52 0 90 -38t38 -90v-307l266 153q46 26 97.5 12.5t77.5 -59.5l64 -110q26 -46 12.5 -97.5t-59.5 -77.5l-266 -154z" />
|
|
||||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM896 161v190q0 14 -9 23.5t-22 9.5h-192q-13 0 -23 -10t-10 -23v-190q0 -13 10 -23t23 -10h192 q13 0 22 9.5t9 23.5zM894 505l18 621q0 12 -10 18q-10 8 -24 8h-220q-14 0 -24 -8q-10 -6 -10 -18l17 -621q0 -10 10 -17.5t24 -7.5h185q14 0 23.5 7.5t10.5 17.5z" />
|
|
||||||
<glyph unicode="" d="M928 180v56v468v192h-320v-192v-468v-56q0 -25 18 -38.5t46 -13.5h192q28 0 46 13.5t18 38.5zM472 1024h195l-126 161q-26 31 -69 31q-40 0 -68 -28t-28 -68t28 -68t68 -28zM1160 1120q0 40 -28 68t-68 28q-43 0 -69 -31l-125 -161h194q40 0 68 28t28 68zM1536 864v-320 q0 -14 -9 -23t-23 -9h-96v-416q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v416h-96q-14 0 -23 9t-9 23v320q0 14 9 23t23 9h440q-93 0 -158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5q107 0 168 -77l128 -165l128 165q61 77 168 77q93 0 158.5 -65.5t65.5 -158.5 t-65.5 -158.5t-158.5 -65.5h440q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 832q0 26 -19 45t-45 19q-172 0 -318 -49.5t-259.5 -134t-235.5 -219.5q-19 -21 -19 -45q0 -26 19 -45t45 -19q24 0 45 19q27 24 74 71t67 66q137 124 268.5 176t313.5 52q26 0 45 19t19 45zM1792 1030q0 -95 -20 -193q-46 -224 -184.5 -383t-357.5 -268 q-214 -108 -438 -108q-148 0 -286 47q-15 5 -88 42t-96 37q-16 0 -39.5 -32t-45 -70t-52.5 -70t-60 -32q-30 0 -51 11t-31 24t-27 42q-2 4 -6 11t-5.5 10t-3 9.5t-1.5 13.5q0 35 31 73.5t68 65.5t68 56t31 48q0 4 -14 38t-16 44q-9 51 -9 104q0 115 43.5 220t119 184.5 t170.5 139t204 95.5q55 18 145 25.5t179.5 9t178.5 6t163.5 24t113.5 56.5l29.5 29.5t29.5 28t27 20t36.5 16t43.5 4.5q39 0 70.5 -46t47.5 -112t24 -124t8 -96z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 -160v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-1344q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h1344q13 0 22.5 -9.5t9.5 -22.5zM1152 896q0 -78 -24.5 -144t-64 -112.5t-87.5 -88t-96 -77.5t-87.5 -72t-64 -81.5t-24.5 -96.5q0 -96 67 -224l-4 1l1 -1 q-90 41 -160 83t-138.5 100t-113.5 122.5t-72.5 150.5t-27.5 184q0 78 24.5 144t64 112.5t87.5 88t96 77.5t87.5 72t64 81.5t24.5 96.5q0 94 -66 224l3 -1l-1 1q90 -41 160 -83t138.5 -100t113.5 -122.5t72.5 -150.5t27.5 -184z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 576q-152 236 -381 353q61 -104 61 -225q0 -185 -131.5 -316.5t-316.5 -131.5t-316.5 131.5t-131.5 316.5q0 121 61 225q-229 -117 -381 -353q133 -205 333.5 -326.5t434.5 -121.5t434.5 121.5t333.5 326.5zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5 t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1792 576q0 -34 -20 -69q-140 -230 -376.5 -368.5t-499.5 -138.5t-499.5 139t-376.5 368q-20 35 -20 69t20 69q140 229 376.5 368t499.5 139t499.5 -139t376.5 -368q20 -35 20 -69z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M555 201l78 141q-87 63 -136 159t-49 203q0 121 61 225q-229 -117 -381 -353q167 -258 427 -375zM944 960q0 20 -14 34t-34 14q-125 0 -214.5 -89.5t-89.5 -214.5q0 -20 14 -34t34 -14t34 14t14 34q0 86 61 147t147 61q20 0 34 14t14 34zM1307 1151q0 -7 -1 -9 q-105 -188 -315 -566t-316 -567l-49 -89q-10 -16 -28 -16q-12 0 -134 70q-16 10 -16 28q0 12 44 87q-143 65 -263.5 173t-208.5 245q-20 31 -20 69t20 69q153 235 380 371t496 136q89 0 180 -17l54 97q10 16 28 16q5 0 18 -6t31 -15.5t33 -18.5t31.5 -18.5t19.5 -11.5 q16 -10 16 -27zM1344 704q0 -139 -79 -253.5t-209 -164.5l280 502q8 -45 8 -84zM1792 576q0 -35 -20 -69q-39 -64 -109 -145q-150 -172 -347.5 -267t-419.5 -95l74 132q212 18 392.5 137t301.5 307q-115 179 -282 294l63 112q95 -64 182.5 -153t144.5 -184q20 -34 20 -69z " />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 161v190q0 14 -9.5 23.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -23.5v-190q0 -14 9.5 -23.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 23.5zM1022 535l18 459q0 12 -10 19q-13 11 -24 11h-220q-11 0 -24 -11q-10 -7 -10 -21l17 -457q0 -10 10 -16.5t24 -6.5h185 q14 0 23.5 6.5t10.5 16.5zM1008 1469l768 -1408q35 -63 -2 -126q-17 -29 -46.5 -46t-63.5 -17h-1536q-34 0 -63.5 17t-46.5 46q-37 63 -2 126l768 1408q17 31 47 49t65 18t65 -18t47 -49z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1376 1376q44 -52 12 -148t-108 -172l-161 -161l160 -696q5 -19 -12 -33l-128 -96q-7 -6 -19 -6q-4 0 -7 1q-15 3 -21 16l-279 508l-259 -259l53 -194q5 -17 -8 -31l-96 -96q-9 -9 -23 -9h-2q-15 2 -24 13l-189 252l-252 189q-11 7 -13 23q-1 13 9 25l96 97q9 9 23 9 q6 0 8 -1l194 -53l259 259l-508 279q-14 8 -17 24q-2 16 9 27l128 128q14 13 30 8l665 -159l160 160q76 76 172 108t148 -12z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M128 -128h288v288h-288v-288zM480 -128h320v288h-320v-288zM128 224h288v320h-288v-320zM480 224h320v320h-320v-320zM128 608h288v288h-288v-288zM864 -128h320v288h-320v-288zM480 608h320v288h-320v-288zM1248 -128h288v288h-288v-288zM864 224h320v320h-320v-320z M512 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1248 224h288v320h-288v-320zM864 608h320v288h-320v-288zM1248 608h288v288h-288v-288zM1280 1088v288q0 13 -9.5 22.5t-22.5 9.5h-64 q-13 0 -22.5 -9.5t-9.5 -22.5v-288q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47 h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M666 1055q-60 -92 -137 -273q-22 45 -37 72.5t-40.5 63.5t-51 56.5t-63 35t-81.5 14.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q250 0 410 -225zM1792 256q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5v192q-32 0 -85 -0.5t-81 -1t-73 1 t-71 5t-64 10.5t-63 18.5t-58 28.5t-59 40t-55 53.5t-56 69.5q59 93 136 273q22 -45 37 -72.5t40.5 -63.5t51 -56.5t63 -35t81.5 -14.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23zM1792 1152q0 -14 -9 -23l-320 -320q-9 -9 -23 -9q-13 0 -22.5 9.5t-9.5 22.5 v192h-256q-48 0 -87 -15t-69 -45t-51 -61.5t-45 -77.5q-32 -62 -78 -171q-29 -66 -49.5 -111t-54 -105t-64 -100t-74 -83t-90 -68.5t-106.5 -42t-128 -16.5h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224q48 0 87 15t69 45t51 61.5t45 77.5q32 62 78 171q29 66 49.5 111 t54 105t64 100t74 83t90 68.5t106.5 42t128 16.5h256v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22q-17 -2 -30.5 9t-17.5 29v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281 q0 130 71 248.5t191 204.5t286 136.5t348 50.5q244 0 450 -85.5t326 -233t120 -321.5z" />
|
|
||||||
<glyph unicode="" d="M1536 704v-128q0 -201 -98.5 -362t-274 -251.5t-395.5 -90.5t-395.5 90.5t-274 251.5t-98.5 362v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-128q0 -52 23.5 -90t53.5 -57t71 -30t64 -13t44 -2t44 2t64 13t71 30t53.5 57t23.5 90v128q0 26 19 45t45 19h384 q26 0 45 -19t19 -45zM512 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45zM1536 1344v-384q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h384q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 320q0 -53 -37 -90l-75 -75q-38 -38 -91 -38q-54 0 -90 38l-486 485l-486 -485q-36 -38 -90 -38t-90 38l-75 75q-38 36 -38 90q0 53 38 91l651 651q37 37 90 37q52 0 91 -37l650 -651q38 -38 38 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1611 832q0 -53 -37 -90l-651 -651q-38 -38 -91 -38q-54 0 -90 38l-651 651q-38 36 -38 90q0 53 38 91l74 75q39 37 91 37q53 0 90 -37l486 -486l486 486q37 37 90 37q52 0 91 -37l75 -75q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 32q0 -13 -9.5 -22.5t-22.5 -9.5h-960q-8 0 -13.5 2t-9 7t-5.5 8t-3 11.5t-1 11.5v13v11v160v416h-192q-26 0 -45 19t-19 45q0 24 15 41l320 384q19 22 49 22t49 -22l320 -384q15 -17 15 -41q0 -26 -19 -45t-45 -19h-192v-384h576q16 0 25 -11l160 -192q7 -11 7 -21 zM1920 448q0 -24 -15 -41l-320 -384q-20 -23 -49 -23t-49 23l-320 384q-15 17 -15 41q0 26 19 45t45 19h192v384h-576q-16 0 -25 12l-160 192q-7 9 -7 20q0 13 9.5 22.5t22.5 9.5h960q8 0 13.5 -2t9 -7t5.5 -8t3 -11.5t1 -11.5v-13v-11v-160v-416h192q26 0 45 -19t19 -45z " />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M640 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1536 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1664 1088v-512q0 -24 -16 -42.5t-41 -21.5 l-1044 -122q1 -7 4.5 -21.5t6 -26.5t2.5 -22q0 -16 -24 -64h920q26 0 45 -19t19 -45t-19 -45t-45 -19h-1024q-26 0 -45 19t-19 45q0 14 11 39.5t29.5 59.5t20.5 38l-177 823h-204q-26 0 -45 19t-19 45t19 45t45 19h256q16 0 28.5 -6.5t20 -15.5t13 -24.5t7.5 -26.5 t5.5 -29.5t4.5 -25.5h1201q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1879 584q0 -31 -31 -66l-336 -396q-43 -51 -120.5 -86.5t-143.5 -35.5h-1088q-34 0 -60.5 13t-26.5 43q0 31 31 66l336 396q43 51 120.5 86.5t143.5 35.5h1088q34 0 60.5 -13t26.5 -43zM1536 928v-160h-832q-94 0 -197 -47.5t-164 -119.5l-337 -396l-5 -6q0 4 -0.5 12.5 t-0.5 12.5v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M704 1216q0 -26 -19 -45t-45 -19h-128v-1024h128q26 0 45 -19t19 -45t-19 -45l-256 -256q-19 -19 -45 -19t-45 19l-256 256q-19 19 -19 45t19 45t45 19h128v1024h-128q-26 0 -45 19t-19 45t19 45l256 256q19 19 45 19t45 -19l256 -256q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 640q0 -26 -19 -45l-256 -256q-19 -19 -45 -19t-45 19t-19 45v128h-1024v-128q0 -26 -19 -45t-45 -19t-45 19l-256 256q-19 19 -19 45t19 45l256 256q19 19 45 19t45 -19t19 -45v-128h1024v128q0 26 19 45t45 19t45 -19l256 -256q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M512 512v-384h-256v384h256zM896 1024v-896h-256v896h256zM1280 768v-640h-256v640h256zM1664 1152v-1024h-256v1024h256zM1792 32v1216q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-1216q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5z M1920 1248v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" d="M1280 926q-56 -25 -121 -34q68 40 93 117q-65 -38 -134 -51q-61 66 -153 66q-87 0 -148.5 -61.5t-61.5 -148.5q0 -29 5 -48q-129 7 -242 65t-192 155q-29 -50 -29 -106q0 -114 91 -175q-47 1 -100 26v-2q0 -75 50 -133.5t123 -72.5q-29 -8 -51 -8q-13 0 -39 4 q21 -63 74.5 -104t121.5 -42q-116 -90 -261 -90q-26 0 -50 3q148 -94 322 -94q112 0 210 35.5t168 95t120.5 137t75 162t24.5 168.5q0 18 -1 27q63 45 105 109zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5 t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1307 618l23 219h-198v109q0 49 15.5 68.5t71.5 19.5h110v219h-175q-152 0 -218 -72t-66 -213v-131h-131v-219h131v-635h262v635h175zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M928 704q0 14 -9 23t-23 9q-66 0 -113 -47t-47 -113q0 -14 9 -23t23 -9t23 9t9 23q0 40 28 68t68 28q14 0 23 9t9 23zM1152 574q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM128 0h1536v128h-1536v-128zM1280 574q0 159 -112.5 271.5 t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM256 1216h384v128h-384v-128zM128 1024h1536v118v138h-828l-64 -128h-644v-128zM1792 1280v-1280q0 -53 -37.5 -90.5t-90.5 -37.5h-1536q-53 0 -90.5 37.5t-37.5 90.5v1280 q0 53 37.5 90.5t90.5 37.5h1536q53 0 90.5 -37.5t37.5 -90.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M832 1024q0 80 -56 136t-136 56t-136 -56t-56 -136q0 -42 19 -83q-41 19 -83 19q-80 0 -136 -56t-56 -136t56 -136t136 -56t136 56t56 136q0 42 -19 83q41 -19 83 -19q80 0 136 56t56 136zM1683 320q0 -17 -49 -66t-66 -49q-9 0 -28.5 16t-36.5 33t-38.5 40t-24.5 26 l-96 -96l220 -220q28 -28 28 -68q0 -42 -39 -81t-81 -39q-40 0 -68 28l-671 671q-176 -131 -365 -131q-163 0 -265.5 102.5t-102.5 265.5q0 160 95 313t248 248t313 95q163 0 265.5 -102.5t102.5 -265.5q0 -189 -131 -365l355 -355l96 96q-3 3 -26 24.5t-40 38.5t-33 36.5 t-16 28.5q0 17 49 66t66 49q13 0 23 -10q6 -6 46 -44.5t82 -79.5t86.5 -86t73 -78t28.5 -41z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M896 640q0 106 -75 181t-181 75t-181 -75t-75 -181t75 -181t181 -75t181 75t75 181zM1664 128q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1152q0 52 -38 90t-90 38t-90 -38t-38 -90q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1280 731v-185q0 -10 -7 -19.5t-16 -10.5l-155 -24q-11 -35 -32 -76q34 -48 90 -115q7 -10 7 -20q0 -12 -7 -19q-23 -30 -82.5 -89.5t-78.5 -59.5q-11 0 -21 7l-115 90q-37 -19 -77 -31q-11 -108 -23 -155q-7 -24 -30 -24h-186q-11 0 -20 7.5t-10 17.5 l-23 153q-34 10 -75 31l-118 -89q-7 -7 -20 -7q-11 0 -21 8q-144 133 -144 160q0 9 7 19q10 14 41 53t47 61q-23 44 -35 82l-152 24q-10 1 -17 9.5t-7 19.5v185q0 10 7 19.5t16 10.5l155 24q11 35 32 76q-34 48 -90 115q-7 11 -7 20q0 12 7 20q22 30 82 89t79 59q11 0 21 -7 l115 -90q34 18 77 32q11 108 23 154q7 24 30 24h186q11 0 20 -7.5t10 -17.5l23 -153q34 -10 75 -31l118 89q8 7 20 7q11 0 21 -8q144 -133 144 -160q0 -9 -7 -19q-12 -16 -42 -54t-45 -60q23 -48 34 -82l152 -23q10 -2 17 -10.5t7 -19.5zM1920 198v-140q0 -16 -149 -31 q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20 t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31zM1920 1222v-140q0 -16 -149 -31q-12 -27 -30 -52q51 -113 51 -138q0 -4 -4 -7q-122 -71 -124 -71q-8 0 -46 47t-52 68 q-20 -2 -30 -2t-30 2q-14 -21 -52 -68t-46 -47q-2 0 -124 71q-4 3 -4 7q0 25 51 138q-18 25 -30 52q-149 15 -149 31v140q0 16 149 31q13 29 30 52q-51 113 -51 138q0 4 4 7q4 2 35 20t59 34t30 16q8 0 46 -46.5t52 -67.5q20 2 30 2t30 -2q51 71 92 112l6 2q4 0 124 -70 q4 -3 4 -7q0 -25 -51 -138q17 -23 30 -52q149 -15 149 -31z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1408 768q0 -139 -94 -257t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224 q0 139 94 257t256.5 186.5t353.5 68.5t353.5 -68.5t256.5 -186.5t94 -257zM1792 512q0 -120 -71 -224.5t-195 -176.5q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7 q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230z" />
|
|
||||||
<glyph unicode="" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 768q0 51 -39 89.5t-89 38.5h-352q0 58 48 159.5t48 160.5q0 98 -32 145t-128 47q-26 -26 -38 -85t-30.5 -125.5t-59.5 -109.5q-22 -23 -77 -91q-4 -5 -23 -30t-31.5 -41t-34.5 -42.5 t-40 -44t-38.5 -35.5t-40 -27t-35.5 -9h-32v-640h32q13 0 31.5 -3t33 -6.5t38 -11t35 -11.5t35.5 -12.5t29 -10.5q211 -73 342 -73h121q192 0 192 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5q32 1 53.5 47t21.5 81zM1536 769 q0 -89 -49 -163q9 -33 9 -69q0 -77 -38 -144q3 -21 3 -43q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5h-36h-93q-96 0 -189.5 22.5t-216.5 65.5q-116 40 -138 40h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h274q36 24 137 155q58 75 107 128 q24 25 35.5 85.5t30.5 126.5t62 108q39 37 90 37q84 0 151 -32.5t102 -101.5t35 -186q0 -93 -48 -192h176q104 0 180 -76t76 -179z" />
|
|
||||||
<glyph unicode="" d="M256 1088q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 512q0 35 -21.5 81t-53.5 47q15 17 25 47.5t10 55.5q0 69 -53 119q18 32 18 69t-17.5 73.5t-47.5 52.5q5 30 5 56q0 85 -49 126t-136 41h-128q-131 0 -342 -73q-5 -2 -29 -10.5 t-35.5 -12.5t-35 -11.5t-38 -11t-33 -6.5t-31.5 -3h-32v-640h32q16 0 35.5 -9t40 -27t38.5 -35.5t40 -44t34.5 -42.5t31.5 -41t23 -30q55 -68 77 -91q41 -43 59.5 -109.5t30.5 -125.5t38 -85q96 0 128 47t32 145q0 59 -48 160.5t-48 159.5h352q50 0 89 38.5t39 89.5z M1536 511q0 -103 -76 -179t-180 -76h-176q48 -99 48 -192q0 -118 -35 -186q-35 -69 -102 -101.5t-151 -32.5q-51 0 -90 37q-34 33 -54 82t-25.5 90.5t-17.5 84.5t-31 64q-48 50 -107 127q-101 131 -137 155h-274q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5 h288q22 0 138 40q128 44 223 66t200 22h112q140 0 226.5 -79t85.5 -216v-5q60 -77 60 -178q0 -22 -3 -43q38 -67 38 -144q0 -36 -9 -69q49 -74 49 -163z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="896" d="M832 1504v-1339l-449 -236q-22 -12 -40 -12q-21 0 -31.5 14.5t-10.5 35.5q0 6 2 20l86 500l-364 354q-25 27 -25 48q0 37 56 46l502 73l225 455q19 41 49 41z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 940q0 81 -21.5 143t-55 98.5t-81.5 59.5t-94 31t-98 8t-112 -25.5t-110.5 -64t-86.5 -72t-60 -61.5q-18 -22 -49 -22t-49 22q-24 28 -60 61.5t-86.5 72t-110.5 64t-112 25.5t-98 -8t-94 -31t-81.5 -59.5t-55 -98.5t-21.5 -143q0 -168 187 -355l581 -560l580 559 q188 188 188 356zM1792 940q0 -221 -229 -450l-623 -600q-18 -18 -44 -18t-44 18l-624 602q-10 8 -27.5 26t-55.5 65.5t-68 97.5t-53.5 121t-23.5 138q0 220 127 344t351 124q62 0 126.5 -21.5t120 -58t95.5 -68.5t76 -68q36 36 76 68t95.5 68.5t120 58t126.5 21.5 q224 0 351 -124t127 -344z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M640 96q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-119 0 -203.5 84.5t-84.5 203.5v704q0 119 84.5 203.5t203.5 84.5h320q13 0 22.5 -9.5t9.5 -22.5q0 -4 1 -20t0.5 -26.5t-3 -23.5t-10 -19.5t-20.5 -6.5h-320q-66 0 -113 -47t-47 -113v-704 q0 -66 47 -113t113 -47h288h11h13t11.5 -1t11.5 -3t8 -5.5t7 -9t2 -13.5zM1568 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" d="M237 122h231v694h-231v-694zM483 1030q-1 52 -36 86t-93 34t-94.5 -34t-36.5 -86q0 -51 35.5 -85.5t92.5 -34.5h1q59 0 95 34.5t36 85.5zM1068 122h231v398q0 154 -73 233t-193 79q-136 0 -209 -117h2v101h-231q3 -66 0 -694h231v388q0 38 7 56q15 35 45 59.5t74 24.5 q116 0 116 -157v-371zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M480 672v448q0 14 -9 23t-23 9t-23 -9t-9 -23v-448q0 -14 9 -23t23 -9t23 9t9 23zM1152 320q0 -26 -19 -45t-45 -19h-429l-51 -483q-2 -12 -10.5 -20.5t-20.5 -8.5h-1q-27 0 -32 27l-76 485h-404q-26 0 -45 19t-19 45q0 123 78.5 221.5t177.5 98.5v512q-52 0 -90 38 t-38 90t38 90t90 38h640q52 0 90 -38t38 -90t-38 -90t-90 -38v-512q99 0 177.5 -98.5t78.5 -221.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
|
|
||||||
<glyph unicode="" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1120 1280h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v832q0 66 -47 113t-113 47zM1408 1120v-832q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832 q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1152 1280h-1024v-1242l423 406l89 85l89 -85l423 -406v1242zM1164 1408q23 0 44 -9q33 -13 52.5 -41t19.5 -62v-1289q0 -34 -19.5 -62t-52.5 -41q-19 -8 -44 -8q-48 0 -83 32l-441 424l-441 -424q-36 -33 -83 -33q-23 0 -44 9q-33 13 -52.5 41t-19.5 62v1289 q0 34 19.5 62t52.5 41q21 9 44 9h1048z" />
|
|
||||||
<glyph unicode="" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M511 980h257l-30 -284h-227v-824h-341v824h-170v284h170v171q0 182 86 275.5t283 93.5h227v-284h-142q-39 0 -62.5 -6.5t-34 -23.5t-13.5 -34.5t-3 -49.5v-142z" />
|
|
||||||
<glyph unicode="" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
|
|
||||||
<glyph unicode="" d="M1040 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1296 320q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5zM1408 160v320q0 13 -9.5 22.5t-22.5 9.5 h-1216q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h1216q13 0 22.5 9.5t9.5 22.5zM178 640h1180l-157 482q-4 13 -16 21.5t-26 8.5h-782q-14 0 -26 -8.5t-16 -21.5zM1536 480v-320q0 -66 -47 -113t-113 -47h-1216q-66 0 -113 47t-47 113v320q0 25 16 75 l197 606q17 53 63 86t101 33h782q55 0 101 -33t63 -86l197 -606q16 -50 16 -75z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 896q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5v-384q0 -52 -38 -90t-90 -38q-417 347 -812 380q-58 -19 -91 -66t-31 -100.5t40 -92.5q-20 -33 -23 -65.5t6 -58t33.5 -55t48 -50t61.5 -50.5q-29 -58 -111.5 -83t-168.5 -11.5t-132 55.5q-7 23 -29.5 87.5 t-32 94.5t-23 89t-15 101t3.5 98.5t22 110.5h-122q-66 0 -113 47t-47 113v192q0 66 47 113t113 47h480q435 0 896 384q52 0 90 -38t38 -90v-384zM1536 292v954q-394 -302 -768 -343v-270q377 -42 768 -341z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM183 128h1298q-164 181 -246.5 411.5t-82.5 484.5q0 256 -320 256t-320 -256q0 -254 -82.5 -484.5t-246.5 -411.5zM1664 128q0 -52 -38 -90t-90 -38 h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
|
|
||||||
<glyph unicode="" d="M1376 640l138 -135q30 -28 20 -70q-12 -41 -52 -51l-188 -48l53 -186q12 -41 -19 -70q-29 -31 -70 -19l-186 53l-48 -188q-10 -40 -51 -52q-12 -2 -19 -2q-31 0 -51 22l-135 138l-135 -138q-28 -30 -70 -20q-41 11 -51 52l-48 188l-186 -53q-41 -12 -70 19q-31 29 -19 70 l53 186l-188 48q-40 10 -52 51q-10 42 20 70l138 135l-138 135q-30 28 -20 70q12 41 52 51l188 48l-53 186q-12 41 19 70q29 31 70 19l186 -53l48 188q10 41 51 51q41 12 70 -19l135 -139l135 139q29 30 70 19q41 -10 51 -51l48 -188l186 53q41 12 70 -19q31 -29 19 -70 l-53 -186l188 -48q40 -10 52 -51q10 -42 -20 -70z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M256 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 768q0 51 -39 89.5t-89 38.5h-576q0 20 15 48.5t33 55t33 68t15 84.5q0 67 -44.5 97.5t-115.5 30.5q-24 0 -90 -139q-24 -44 -37 -65q-40 -64 -112 -145q-71 -81 -101 -106 q-69 -57 -140 -57h-32v-640h32q72 0 167 -32t193.5 -64t179.5 -32q189 0 189 167q0 26 -5 56q30 16 47.5 52.5t17.5 73.5t-18 69q53 50 53 119q0 25 -10 55.5t-25 47.5h331q52 0 90 38t38 90zM1792 769q0 -105 -75.5 -181t-180.5 -76h-169q-4 -62 -37 -119q3 -21 3 -43 q0 -101 -60 -178q1 -139 -85 -219.5t-227 -80.5q-133 0 -322 69q-164 59 -223 59h-288q-53 0 -90.5 37.5t-37.5 90.5v640q0 53 37.5 90.5t90.5 37.5h288q10 0 21.5 4.5t23.5 14t22.5 18t24 22.5t20.5 21.5t19 21.5t14 17q65 74 100 129q13 21 33 62t37 72t40.5 63t55 49.5 t69.5 17.5q125 0 206.5 -67t81.5 -189q0 -68 -22 -128h374q104 0 180 -76t76 -179z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1376 128h32v640h-32q-35 0 -67.5 12t-62.5 37t-50 46t-49 54q-2 3 -3.5 4.5t-4 4.5t-4.5 5q-72 81 -112 145q-14 22 -38 68q-1 3 -10.5 22.5t-18.5 36t-20 35.5t-21.5 30.5t-18.5 11.5q-71 0 -115.5 -30.5t-44.5 -97.5q0 -43 15 -84.5t33 -68t33 -55t15 -48.5h-576 q-50 0 -89 -38.5t-39 -89.5q0 -52 38 -90t90 -38h331q-15 -17 -25 -47.5t-10 -55.5q0 -69 53 -119q-18 -32 -18 -69t17.5 -73.5t47.5 -52.5q-4 -24 -4 -56q0 -85 48.5 -126t135.5 -41q84 0 183 32t194 64t167 32zM1664 192q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45 t45 -19t45 19t19 45zM1792 768v-640q0 -53 -37.5 -90.5t-90.5 -37.5h-288q-59 0 -223 -59q-190 -69 -317 -69q-142 0 -230 77.5t-87 217.5l1 5q-61 76 -61 178q0 22 3 43q-33 57 -37 119h-169q-105 0 -180.5 76t-75.5 181q0 103 76 179t180 76h374q-22 60 -22 128 q0 122 81.5 189t206.5 67q38 0 69.5 -17.5t55 -49.5t40.5 -63t37 -72t33 -62q35 -55 100 -129q2 -3 14 -17t19 -21.5t20.5 -21.5t24 -22.5t22.5 -18t23.5 -14t21.5 -4.5h288q53 0 90.5 -37.5t37.5 -90.5z" />
|
|
||||||
<glyph unicode="" d="M1280 -64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 700q0 189 -167 189q-26 0 -56 -5q-16 30 -52.5 47.5t-73.5 17.5t-69 -18q-50 53 -119 53q-25 0 -55.5 -10t-47.5 -25v331q0 52 -38 90t-90 38q-51 0 -89.5 -39t-38.5 -89v-576 q-20 0 -48.5 15t-55 33t-68 33t-84.5 15q-67 0 -97.5 -44.5t-30.5 -115.5q0 -24 139 -90q44 -24 65 -37q64 -40 145 -112q81 -71 106 -101q57 -69 57 -140v-32h640v32q0 72 32 167t64 193.5t32 179.5zM1536 705q0 -133 -69 -322q-59 -164 -59 -223v-288q0 -53 -37.5 -90.5 t-90.5 -37.5h-640q-53 0 -90.5 37.5t-37.5 90.5v288q0 10 -4.5 21.5t-14 23.5t-18 22.5t-22.5 24t-21.5 20.5t-21.5 19t-17 14q-74 65 -129 100q-21 13 -62 33t-72 37t-63 40.5t-49.5 55t-17.5 69.5q0 125 67 206.5t189 81.5q68 0 128 -22v374q0 104 76 180t179 76 q105 0 181 -75.5t76 -180.5v-169q62 -4 119 -37q21 3 43 3q101 0 178 -60q139 1 219.5 -85t80.5 -227z" />
|
|
||||||
<glyph unicode="" d="M1408 576q0 84 -32 183t-64 194t-32 167v32h-640v-32q0 -35 -12 -67.5t-37 -62.5t-46 -50t-54 -49q-9 -8 -14 -12q-81 -72 -145 -112q-22 -14 -68 -38q-3 -1 -22.5 -10.5t-36 -18.5t-35.5 -20t-30.5 -21.5t-11.5 -18.5q0 -71 30.5 -115.5t97.5 -44.5q43 0 84.5 15t68 33 t55 33t48.5 15v-576q0 -50 38.5 -89t89.5 -39q52 0 90 38t38 90v331q46 -35 103 -35q69 0 119 53q32 -18 69 -18t73.5 17.5t52.5 47.5q24 -4 56 -4q85 0 126 48.5t41 135.5zM1280 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 580 q0 -142 -77.5 -230t-217.5 -87l-5 1q-76 -61 -178 -61q-22 0 -43 3q-54 -30 -119 -37v-169q0 -105 -76 -180.5t-181 -75.5q-103 0 -179 76t-76 180v374q-54 -22 -128 -22q-121 0 -188.5 81.5t-67.5 206.5q0 38 17.5 69.5t49.5 55t63 40.5t72 37t62 33q55 35 129 100 q3 2 17 14t21.5 19t21.5 20.5t22.5 24t18 22.5t14 23.5t4.5 21.5v288q0 53 37.5 90.5t90.5 37.5h640q53 0 90.5 -37.5t37.5 -90.5v-288q0 -59 59 -223q69 -190 69 -317z" />
|
|
||||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-502l189 189q19 19 19 45t-19 45l-91 91q-18 18 -45 18t-45 -18l-362 -362l-91 -91q-18 -18 -18 -45t18 -45l91 -91l362 -362q18 -18 45 -18t45 18l91 91q18 18 18 45t-18 45l-189 189h502q26 0 45 19t19 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1285 640q0 27 -18 45l-91 91l-362 362q-18 18 -45 18t-45 -18l-91 -91q-18 -18 -18 -45t18 -45l189 -189h-502q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h502l-189 -189q-19 -19 -19 -45t19 -45l91 -91q18 -18 45 -18t45 18l362 362l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1284 641q0 27 -18 45l-362 362l-91 91q-18 18 -45 18t-45 -18l-91 -91l-362 -362q-18 -18 -18 -45t18 -45l91 -91q18 -18 45 -18t45 18l189 189v-502q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v502l189 -189q19 -19 45 -19t45 19l91 91q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1284 639q0 27 -18 45l-91 91q-18 18 -45 18t-45 -18l-189 -189v502q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-502l-189 189q-19 19 -45 19t-45 -19l-91 -91q-18 -18 -18 -45t18 -45l362 -362l91 -91q18 -18 45 -18t45 18l91 91l362 362q18 18 18 45zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1042 887q-2 -1 -9.5 -9.5t-13.5 -9.5q2 0 4.5 5t5 11t3.5 7q6 7 22 15q14 6 52 12q34 8 51 -11 q-2 2 9.5 13t14.5 12q3 2 15 4.5t15 7.5l2 22q-12 -1 -17.5 7t-6.5 21q0 -2 -6 -8q0 7 -4.5 8t-11.5 -1t-9 -1q-10 3 -15 7.5t-8 16.5t-4 15q-2 5 -9.5 10.5t-9.5 10.5q-1 2 -2.5 5.5t-3 6.5t-4 5.5t-5.5 2.5t-7 -5t-7.5 -10t-4.5 -5q-3 2 -6 1.5t-4.5 -1t-4.5 -3t-5 -3.5 q-3 -2 -8.5 -3t-8.5 -2q15 5 -1 11q-10 4 -16 3q9 4 7.5 12t-8.5 14h5q-1 4 -8.5 8.5t-17.5 8.5t-13 6q-8 5 -34 9.5t-33 0.5q-5 -6 -4.5 -10.5t4 -14t3.5 -12.5q1 -6 -5.5 -13t-6.5 -12q0 -7 14 -15.5t10 -21.5q-3 -8 -16 -16t-16 -12q-5 -8 -1.5 -18.5t10.5 -16.5 q2 -2 1.5 -4t-3.5 -4.5t-5.5 -4t-6.5 -3.5l-3 -2q-11 -5 -20.5 6t-13.5 26q-7 25 -16 30q-23 8 -29 -1q-5 13 -41 26q-25 9 -58 4q6 1 0 15q-7 15 -19 12q3 6 4 17.5t1 13.5q3 13 12 23q1 1 7 8.5t9.5 13.5t0.5 6q35 -4 50 11q5 5 11.5 17t10.5 17q9 6 14 5.5t14.5 -5.5 t14.5 -5q14 -1 15.5 11t-7.5 20q12 -1 3 17q-5 7 -8 9q-12 4 -27 -5q-8 -4 2 -8q-1 1 -9.5 -10.5t-16.5 -17.5t-16 5q-1 1 -5.5 13.5t-9.5 13.5q-8 0 -16 -15q3 8 -11 15t-24 8q19 12 -8 27q-7 4 -20.5 5t-19.5 -4q-5 -7 -5.5 -11.5t5 -8t10.5 -5.5t11.5 -4t8.5 -3 q14 -10 8 -14q-2 -1 -8.5 -3.5t-11.5 -4.5t-6 -4q-3 -4 0 -14t-2 -14q-5 5 -9 17.5t-7 16.5q7 -9 -25 -6l-10 1q-4 0 -16 -2t-20.5 -1t-13.5 8q-4 8 0 20q1 4 4 2q-4 3 -11 9.5t-10 8.5q-46 -15 -94 -41q6 -1 12 1q5 2 13 6.5t10 5.5q34 14 42 7l5 5q14 -16 20 -25 q-7 4 -30 1q-20 -6 -22 -12q7 -12 5 -18q-4 3 -11.5 10t-14.5 11t-15 5q-16 0 -22 -1q-146 -80 -235 -222q7 -7 12 -8q4 -1 5 -9t2.5 -11t11.5 3q9 -8 3 -19q1 1 44 -27q19 -17 21 -21q3 -11 -10 -18q-1 2 -9 9t-9 4q-3 -5 0.5 -18.5t10.5 -12.5q-7 0 -9.5 -16t-2.5 -35.5 t-1 -23.5l2 -1q-3 -12 5.5 -34.5t21.5 -19.5q-13 -3 20 -43q6 -8 8 -9q3 -2 12 -7.5t15 -10t10 -10.5q4 -5 10 -22.5t14 -23.5q-2 -6 9.5 -20t10.5 -23q-1 0 -2.5 -1t-2.5 -1q3 -7 15.5 -14t15.5 -13q1 -3 2 -10t3 -11t8 -2q2 20 -24 62q-15 25 -17 29q-3 5 -5.5 15.5 t-4.5 14.5q2 0 6 -1.5t8.5 -3.5t7.5 -4t2 -3q-3 -7 2 -17.5t12 -18.5t17 -19t12 -13q6 -6 14 -19.5t0 -13.5q9 0 20 -10t17 -20q5 -8 8 -26t5 -24q2 -7 8.5 -13.5t12.5 -9.5l16 -8t13 -7q5 -2 18.5 -10.5t21.5 -11.5q10 -4 16 -4t14.5 2.5t13.5 3.5q15 2 29 -15t21 -21 q36 -19 55 -11q-2 -1 0.5 -7.5t8 -15.5t9 -14.5t5.5 -8.5q5 -6 18 -15t18 -15q6 4 7 9q-3 -8 7 -20t18 -10q14 3 14 32q-31 -15 -49 18q0 1 -2.5 5.5t-4 8.5t-2.5 8.5t0 7.5t5 3q9 0 10 3.5t-2 12.5t-4 13q-1 8 -11 20t-12 15q-5 -9 -16 -8t-16 9q0 -1 -1.5 -5.5t-1.5 -6.5 q-13 0 -15 1q1 3 2.5 17.5t3.5 22.5q1 4 5.5 12t7.5 14.5t4 12.5t-4.5 9.5t-17.5 2.5q-19 -1 -26 -20q-1 -3 -3 -10.5t-5 -11.5t-9 -7q-7 -3 -24 -2t-24 5q-13 8 -22.5 29t-9.5 37q0 10 2.5 26.5t3 25t-5.5 24.5q3 2 9 9.5t10 10.5q2 1 4.5 1.5t4.5 0t4 1.5t3 6q-1 1 -4 3 q-3 3 -4 3q7 -3 28.5 1.5t27.5 -1.5q15 -11 22 2q0 1 -2.5 9.5t-0.5 13.5q5 -27 29 -9q3 -3 15.5 -5t17.5 -5q3 -2 7 -5.5t5.5 -4.5t5 0.5t8.5 6.5q10 -14 12 -24q11 -40 19 -44q7 -3 11 -2t4.5 9.5t0 14t-1.5 12.5l-1 8v18l-1 8q-15 3 -18.5 12t1.5 18.5t15 18.5q1 1 8 3.5 t15.5 6.5t12.5 8q21 19 15 35q7 0 11 9q-1 0 -5 3t-7.5 5t-4.5 2q9 5 2 16q5 3 7.5 11t7.5 10q9 -12 21 -2q7 8 1 16q5 7 20.5 10.5t18.5 9.5q7 -2 8 2t1 12t3 12q4 5 15 9t13 5l17 11q3 4 0 4q18 -2 31 11q10 11 -6 20q3 6 -3 9.5t-15 5.5q3 1 11.5 0.5t10.5 1.5 q15 10 -7 16q-17 5 -43 -12zM879 10q206 36 351 189q-3 3 -12.5 4.5t-12.5 3.5q-18 7 -24 8q1 7 -2.5 13t-8 9t-12.5 8t-11 7q-2 2 -7 6t-7 5.5t-7.5 4.5t-8.5 2t-10 -1l-3 -1q-3 -1 -5.5 -2.5t-5.5 -3t-4 -3t0 -2.5q-21 17 -36 22q-5 1 -11 5.5t-10.5 7t-10 1.5t-11.5 -7 q-5 -5 -6 -15t-2 -13q-7 5 0 17.5t2 18.5q-3 6 -10.5 4.5t-12 -4.5t-11.5 -8.5t-9 -6.5t-8.5 -5.5t-8.5 -7.5q-3 -4 -6 -12t-5 -11q-2 4 -11.5 6.5t-9.5 5.5q2 -10 4 -35t5 -38q7 -31 -12 -48q-27 -25 -29 -40q-4 -22 12 -26q0 -7 -8 -20.5t-7 -21.5q0 -6 2 -16z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M384 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1028 484l-682 -682q-37 -37 -90 -37q-52 0 -91 37l-106 108q-38 36 -38 90q0 53 38 91l681 681q39 -98 114.5 -173.5t173.5 -114.5zM1662 919q0 -39 -23 -106q-47 -134 -164.5 -217.5 t-258.5 -83.5q-185 0 -316.5 131.5t-131.5 316.5t131.5 316.5t316.5 131.5q58 0 121.5 -16.5t107.5 -46.5q16 -11 16 -28t-16 -28l-293 -169v-224l193 -107q5 3 79 48.5t135.5 81t70.5 35.5q15 0 23.5 -10t8.5 -25z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 128h640v128h-640v-128zM640 640h1024v128h-1024v-128zM1280 1152h384v128h-384v-128zM1792 320v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 832v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19 t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45zM1792 1344v-256q0 -26 -19 -45t-45 -19h-1664q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1664q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1403 1241q17 -41 -14 -70l-493 -493v-742q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-256 256q-19 19 -19 45v486l-493 493q-31 29 -14 70q17 39 59 39h1280q42 0 59 -39z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M640 1280h512v128h-512v-128zM1792 640v-480q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v480h672v-160q0 -26 19 -45t45 -19h320q26 0 45 19t19 45v160h672zM1024 640v-128h-256v128h256zM1792 1120v-384h-1792v384q0 66 47 113t113 47h352v160q0 40 28 68 t68 28h576q40 0 68 -28t28 -68v-160h352q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" d="M1283 995l-355 -355l355 -355l144 144q29 31 70 14q39 -17 39 -59v-448q0 -26 -19 -45t-45 -19h-448q-42 0 -59 40q-17 39 14 69l144 144l-355 355l-355 -355l144 -144q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l144 -144 l355 355l-355 355l-144 -144q-19 -19 -45 -19q-12 0 -24 5q-40 17 -40 59v448q0 26 19 45t45 19h448q42 0 59 -40q17 -39 -14 -69l-144 -144l355 -355l355 355l-144 144q-31 30 -14 69q17 40 59 40h448q26 0 45 -19t19 -45v-448q0 -42 -39 -59q-13 -5 -25 -5q-26 0 -45 19z " />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M593 640q-162 -5 -265 -128h-134q-82 0 -138 40.5t-56 118.5q0 353 124 353q6 0 43.5 -21t97.5 -42.5t119 -21.5q67 0 133 23q-5 -37 -5 -66q0 -139 81 -256zM1664 3q0 -120 -73 -189.5t-194 -69.5h-874q-121 0 -194 69.5t-73 189.5q0 53 3.5 103.5t14 109t26.5 108.5 t43 97.5t62 81t85.5 53.5t111.5 20q10 0 43 -21.5t73 -48t107 -48t135 -21.5t135 21.5t107 48t73 48t43 21.5q61 0 111.5 -20t85.5 -53.5t62 -81t43 -97.5t26.5 -108.5t14 -109t3.5 -103.5zM640 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75 t75 -181zM1344 896q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5zM1920 671q0 -78 -56 -118.5t-138 -40.5h-134q-103 123 -265 128q81 117 81 256q0 29 -5 66q66 -23 133 -23q59 0 119 21.5t97.5 42.5 t43.5 21q124 0 124 -353zM1792 1280q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1456 320q0 40 -28 68l-208 208q-28 28 -68 28q-42 0 -72 -32q3 -3 19 -18.5t21.5 -21.5t15 -19t13 -25.5t3.5 -27.5q0 -40 -28 -68t-68 -28q-15 0 -27.5 3.5t-25.5 13t-19 15t-21.5 21.5t-18.5 19q-33 -31 -33 -73q0 -40 28 -68l206 -207q27 -27 68 -27q40 0 68 26 l147 146q28 28 28 67zM753 1025q0 40 -28 68l-206 207q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l208 -208q27 -27 68 -27q42 0 72 31q-3 3 -19 18.5t-21.5 21.5t-15 19t-13 25.5t-3.5 27.5q0 40 28 68t68 28q15 0 27.5 -3.5t25.5 -13t19 -15 t21.5 -21.5t18.5 -19q33 31 33 73zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-206 207q-83 83 -83 203q0 123 88 209l-88 88q-86 -88 -208 -88q-120 0 -204 84l-208 208q-84 84 -84 204t85 203l147 146q83 83 203 83q121 0 204 -85l206 -207 q83 -83 83 -203q0 -123 -88 -209l88 -88q86 88 208 88q120 0 204 -84l208 -208q84 -84 84 -204z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088q-185 0 -316.5 131.5t-131.5 316.5q0 132 71 241.5t187 163.5q-2 28 -2 43q0 212 150 362t362 150q158 0 286.5 -88t187.5 -230q70 62 166 62q106 0 181 -75t75 -181q0 -75 -41 -138q129 -30 213 -134.5t84 -239.5z " />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1527 88q56 -89 21.5 -152.5t-140.5 -63.5h-1152q-106 0 -140.5 63.5t21.5 152.5l503 793v399h-64q-26 0 -45 19t-19 45t19 45t45 19h512q26 0 45 -19t19 -45t-19 -45t-45 -19h-64v-399zM748 813l-272 -429h712l-272 429l-20 31v37v399h-128v-399v-37z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M960 640q26 0 45 -19t19 -45t-19 -45t-45 -19t-45 19t-19 45t19 45t45 19zM1260 576l507 -398q28 -20 25 -56q-5 -35 -35 -51l-128 -64q-13 -7 -29 -7q-17 0 -31 8l-690 387l-110 -66q-8 -4 -12 -5q14 -49 10 -97q-7 -77 -56 -147.5t-132 -123.5q-132 -84 -277 -84 q-136 0 -222 78q-90 84 -79 207q7 76 56 147t131 124q132 84 278 84q83 0 151 -31q9 13 22 22l122 73l-122 73q-13 9 -22 22q-68 -31 -151 -31q-146 0 -278 84q-82 53 -131 124t-56 147q-5 59 15.5 113t63.5 93q85 79 222 79q145 0 277 -84q83 -52 132 -123t56 -148 q4 -48 -10 -97q4 -1 12 -5l110 -66l690 387q14 8 31 8q16 0 29 -7l128 -64q30 -16 35 -51q3 -36 -25 -56zM579 836q46 42 21 108t-106 117q-92 59 -192 59q-74 0 -113 -36q-46 -42 -21 -108t106 -117q92 -59 192 -59q74 0 113 36zM494 91q81 51 106 117t-21 108 q-39 36 -113 36q-100 0 -192 -59q-81 -51 -106 -117t21 -108q39 -36 113 -36q100 0 192 59zM672 704l96 -58v11q0 36 33 56l14 8l-79 47l-26 -26q-3 -3 -10 -11t-12 -12q-2 -2 -4 -3.5t-3 -2.5zM896 480l96 -32l736 576l-128 64l-768 -431v-113l-160 -96l9 -8q2 -2 7 -6 q4 -4 11 -12t11 -12l26 -26zM1600 64l128 64l-520 408l-177 -138q-2 -3 -13 -7z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1696 1152q40 0 68 -28t28 -68v-1216q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v288h-544q-40 0 -68 28t-28 68v672q0 40 20 88t48 76l408 408q28 28 76 48t88 20h416q40 0 68 -28t28 -68v-328q68 40 128 40h416zM1152 939l-299 -299h299v299zM512 1323l-299 -299 h299v299zM708 676l316 316v416h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h512v256q0 40 20 88t48 76zM1664 -128v1152h-384v-416q0 -40 -28 -68t-68 -28h-416v-640h896z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1404 151q0 -117 -79 -196t-196 -79q-135 0 -235 100l-777 776q-113 115 -113 271q0 159 110 270t269 111q158 0 273 -113l605 -606q10 -10 10 -22q0 -16 -30.5 -46.5t-46.5 -30.5q-13 0 -23 10l-606 607q-79 77 -181 77q-106 0 -179 -75t-73 -181q0 -105 76 -181 l776 -777q63 -63 145 -63q64 0 106 42t42 106q0 82 -63 145l-581 581q-26 24 -60 24q-29 0 -48 -19t-19 -48q0 -32 25 -59l410 -410q10 -10 10 -22q0 -16 -31 -47t-47 -31q-12 0 -22 10l-410 410q-63 61 -63 149q0 82 57 139t139 57q88 0 149 -63l581 -581q100 -98 100 -235 z" />
|
|
||||||
<glyph unicode="" d="M384 0h768v384h-768v-384zM1280 0h128v896q0 14 -10 38.5t-20 34.5l-281 281q-10 10 -34 20t-39 10v-416q0 -40 -28 -68t-68 -28h-576q-40 0 -68 28t-28 68v416h-128v-1280h128v416q0 40 28 68t68 28h832q40 0 68 -28t28 -68v-416zM896 928v320q0 13 -9.5 22.5t-22.5 9.5 h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5zM1536 896v-928q0 -40 -28 -68t-68 -28h-1344q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h928q40 0 88 -20t76 -48l280 -280q28 -28 48 -76t20 -88z" />
|
|
||||||
<glyph unicode="" d="M1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1536 192v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 704v-128q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1536 1216v-128q0 -26 -19 -45 t-45 -19h-1408q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M384 128q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 640q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 224v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5 t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1152q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z M1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M381 -84q0 -80 -54.5 -126t-135.5 -46q-106 0 -172 66l57 88q49 -45 106 -45q29 0 50.5 14.5t21.5 42.5q0 64 -105 56l-26 56q8 10 32.5 43.5t42.5 54t37 38.5v1q-16 0 -48.5 -1t-48.5 -1v-53h-106v152h333v-88l-95 -115q51 -12 81 -49t30 -88zM383 543v-159h-362 q-6 36 -6 54q0 51 23.5 93t56.5 68t66 47.5t56.5 43.5t23.5 45q0 25 -14.5 38.5t-39.5 13.5q-46 0 -81 -58l-85 59q24 51 71.5 79.5t105.5 28.5q73 0 123 -41.5t50 -112.5q0 -50 -34 -91.5t-75 -64.5t-75.5 -50.5t-35.5 -52.5h127v60h105zM1792 224v-192q0 -13 -9.5 -22.5 t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM384 1123v-99h-335v99h107q0 41 0.5 122t0.5 121v12h-2q-8 -17 -50 -54l-71 76l136 127h106v-404h108zM1792 736v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5 t-9.5 22.5v192q0 14 9 23t23 9h1216q13 0 22.5 -9.5t9.5 -22.5zM1792 1248v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1216q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1216q13 0 22.5 -9.5t9.5 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1760 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-1728q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h1728zM483 704q-28 35 -51 80q-48 97 -48 188q0 181 134 309q133 127 393 127q50 0 167 -19q66 -12 177 -48q10 -38 21 -118q14 -123 14 -183q0 -18 -5 -45l-12 -3l-84 6 l-14 2q-50 149 -103 205q-88 91 -210 91q-114 0 -182 -59q-67 -58 -67 -146q0 -73 66 -140t279 -129q69 -20 173 -66q58 -28 95 -52h-743zM990 448h411q7 -39 7 -92q0 -111 -41 -212q-23 -55 -71 -104q-37 -35 -109 -81q-80 -48 -153 -66q-80 -21 -203 -21q-114 0 -195 23 l-140 40q-57 16 -72 28q-8 8 -8 22v13q0 108 -2 156q-1 30 0 68l2 37v44l102 2q15 -34 30 -71t22.5 -56t12.5 -27q35 -57 80 -94q43 -36 105 -57q59 -22 132 -22q64 0 139 27q77 26 122 86q47 61 47 129q0 84 -81 157q-34 29 -137 71z" />
|
|
||||||
<glyph unicode="" d="M48 1313q-37 2 -45 4l-3 88q13 1 40 1q60 0 112 -4q132 -7 166 -7q86 0 168 3q116 4 146 5q56 0 86 2l-1 -14l2 -64v-9q-60 -9 -124 -9q-60 0 -79 -25q-13 -14 -13 -132q0 -13 0.5 -32.5t0.5 -25.5l1 -229l14 -280q6 -124 51 -202q35 -59 96 -92q88 -47 177 -47 q104 0 191 28q56 18 99 51q48 36 65 64q36 56 53 114q21 73 21 229q0 79 -3.5 128t-11 122.5t-13.5 159.5l-4 59q-5 67 -24 88q-34 35 -77 34l-100 -2l-14 3l2 86h84l205 -10q76 -3 196 10l18 -2q6 -38 6 -51q0 -7 -4 -31q-45 -12 -84 -13q-73 -11 -79 -17q-15 -15 -15 -41 q0 -7 1.5 -27t1.5 -31q8 -19 22 -396q6 -195 -15 -304q-15 -76 -41 -122q-38 -65 -112 -123q-75 -57 -182 -89q-109 -33 -255 -33q-167 0 -284 46q-119 47 -179 122q-61 76 -83 195q-16 80 -16 237v333q0 188 -17 213q-25 36 -147 39zM1536 -96v64q0 14 -9 23t-23 9h-1472 q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h1472q14 0 23 9t9 23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M512 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 160v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23 v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM512 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 160v192 q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1024 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 544v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192 q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1536 928v192q0 14 -9 23t-23 9h-320q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h320q14 0 23 9t9 23zM1664 1248v-1088q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1344q66 0 113 -47t47 -113 z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1190 955l293 293l-107 107l-293 -293zM1637 1248q0 -27 -18 -45l-1286 -1286q-18 -18 -45 -18t-45 18l-198 198q-18 18 -18 45t18 45l1286 1286q18 18 45 18t45 -18l198 -198q18 -18 18 -45zM286 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM636 1276 l196 -60l-196 -60l-60 -196l-60 196l-196 60l196 60l60 196zM1566 798l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98zM926 1438l98 -30l-98 -30l-30 -98l-30 98l-98 30l98 30l30 98z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
|
|
||||||
<glyph unicode="" d="M678 -57q0 -38 -10 -71h-380q-95 0 -171.5 56.5t-103.5 147.5q24 45 69 77.5t100 49.5t107 24t107 7q32 0 49 -2q6 -4 30.5 -21t33 -23t31 -23t32 -25.5t27.5 -25.5t26.5 -29.5t21 -30.5t17.5 -34.5t9.5 -36t4.5 -40.5zM385 294q-234 -7 -385 -85v433q103 -118 273 -118 q32 0 70 5q-21 -61 -21 -86q0 -67 63 -149zM558 805q0 -100 -43.5 -160.5t-140.5 -60.5q-51 0 -97 26t-78 67.5t-56 93.5t-35.5 104t-11.5 99q0 96 51.5 165t144.5 69q66 0 119 -41t84 -104t47 -130t16 -128zM1536 896v-736q0 -119 -84.5 -203.5t-203.5 -84.5h-468 q39 73 39 157q0 66 -22 122.5t-55.5 93t-72 71t-72 59.5t-55.5 54.5t-22 59.5q0 36 23 68t56 61.5t65.5 64.5t55.5 93t23 131t-26.5 145.5t-75.5 118.5q-6 6 -14 11t-12.5 7.5t-10 9.5t-10.5 17h135l135 64h-437q-138 0 -244.5 -38.5t-182.5 -133.5q0 126 81 213t207 87h960 q119 0 203.5 -84.5t84.5 -203.5v-96h-256v256h-128v-256h-256v-128h256v-256h128v256h256z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M876 71q0 21 -4.5 40.5t-9.5 36t-17.5 34.5t-21 30.5t-26.5 29.5t-27.5 25.5t-32 25.5t-31 23t-33 23t-30.5 21q-17 2 -50 2q-54 0 -106 -7t-108 -25t-98 -46t-69 -75t-27 -107q0 -68 35.5 -121.5t93 -84t120.5 -45.5t127 -15q59 0 112.5 12.5t100.5 39t74.5 73.5 t27.5 110zM756 933q0 60 -16.5 127.5t-47 130.5t-84 104t-119.5 41q-93 0 -144 -69t-51 -165q0 -47 11.5 -99t35.5 -104t56 -93.5t78 -67.5t97 -26q97 0 140.5 60.5t43.5 160.5zM625 1408h437l-135 -79h-135q71 -45 110 -126t39 -169q0 -74 -23 -131.5t-56 -92.5t-66 -64.5 t-56 -61t-23 -67.5q0 -26 16.5 -51t43 -48t58.5 -48t64 -55.5t58.5 -66t43 -85t16.5 -106.5q0 -160 -140 -282q-152 -131 -420 -131q-59 0 -119.5 10t-122 33.5t-108.5 58t-77 89t-30 121.5q0 61 37 135q32 64 96 110.5t145 71t155 36t150 13.5q-64 83 -64 149q0 12 2 23.5 t5 19.5t8 21.5t7 21.5q-40 -5 -70 -5q-149 0 -255.5 98t-106.5 246q0 140 95 250.5t234 141.5q94 20 187 20zM1664 1152v-128h-256v-256h-128v256h-256v128h256v256h128v-256h256z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M640 1088v-896q0 -26 -19 -45t-45 -19t-45 19l-448 448q-19 19 -19 45t19 45l448 448q19 19 45 19t45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M576 640q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19t-19 45v896q0 26 19 45t45 19t45 -19l448 -448q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M160 0h608v1152h-640v-1120q0 -13 9.5 -22.5t22.5 -9.5zM1536 32v1120h-640v-1152h608q13 0 22.5 9.5t9.5 22.5zM1664 1248v-1216q0 -66 -47 -113t-113 -47h-1344q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1344q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45zM1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 448q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 826v-794q0 -66 -47 -113t-113 -47h-1472q-66 0 -113 47t-47 113v794q44 -49 101 -87q362 -246 497 -345q57 -42 92.5 -65.5t94.5 -48t110 -24.5h1h1q51 0 110 24.5t94.5 48t92.5 65.5q170 123 498 345q57 39 100 87zM1792 1120q0 -79 -49 -151t-122 -123 q-376 -261 -468 -325q-10 -7 -42.5 -30.5t-54 -38t-52 -32.5t-57.5 -27t-50 -9h-1h-1q-23 0 -50 9t-57.5 27t-52 32.5t-54 38t-42.5 30.5q-91 64 -262 182.5t-205 142.5q-62 42 -117 115.5t-55 136.5q0 78 41.5 130t118.5 52h1472q65 0 112.5 -47t47.5 -113z" />
|
|
||||||
<glyph unicode="" d="M349 911v-991h-330v991h330zM370 1217q1 -73 -50.5 -122t-135.5 -49h-2q-82 0 -132 49t-50 122q0 74 51.5 122.5t134.5 48.5t133 -48.5t51 -122.5zM1536 488v-568h-329v530q0 105 -40.5 164.5t-126.5 59.5q-63 0 -105.5 -34.5t-63.5 -85.5q-11 -30 -11 -81v-553h-329 q2 399 2 647t-1 296l-1 48h329v-144h-2q20 32 41 56t56.5 52t87 43.5t114.5 15.5q171 0 275 -113.5t104 -332.5z" />
|
|
||||||
<glyph unicode="" d="M1536 640q0 -156 -61 -298t-164 -245t-245 -164t-298 -61q-172 0 -327 72.5t-264 204.5q-7 10 -6.5 22.5t8.5 20.5l137 138q10 9 25 9q16 -2 23 -12q73 -95 179 -147t225 -52q104 0 198.5 40.5t163.5 109.5t109.5 163.5t40.5 198.5t-40.5 198.5t-109.5 163.5 t-163.5 109.5t-198.5 40.5q-98 0 -188 -35.5t-160 -101.5l137 -138q31 -30 14 -69q-17 -40 -59 -40h-448q-26 0 -45 19t-19 45v448q0 42 40 59q39 17 69 -14l130 -129q107 101 244.5 156.5t284.5 55.5q156 0 298 -61t245 -164t164 -245t61 -298z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1771 0q0 -53 -37 -90l-107 -108q-39 -37 -91 -37q-53 0 -90 37l-363 364q-38 36 -38 90q0 53 43 96l-256 256l-126 -126q-14 -14 -34 -14t-34 14q2 -2 12.5 -12t12.5 -13t10 -11.5t10 -13.5t6 -13.5t5.5 -16.5t1.5 -18q0 -38 -28 -68q-3 -3 -16.5 -18t-19 -20.5 t-18.5 -16.5t-22 -15.5t-22 -9t-26 -4.5q-40 0 -68 28l-408 408q-28 28 -28 68q0 13 4.5 26t9 22t15.5 22t16.5 18.5t20.5 19t18 16.5q30 28 68 28q10 0 18 -1.5t16.5 -5.5t13.5 -6t13.5 -10t11.5 -10t13 -12.5t12 -12.5q-14 14 -14 34t14 34l348 348q14 14 34 14t34 -14 q-2 2 -12.5 12t-12.5 13t-10 11.5t-10 13.5t-6 13.5t-5.5 16.5t-1.5 18q0 38 28 68q3 3 16.5 18t19 20.5t18.5 16.5t22 15.5t22 9t26 4.5q40 0 68 -28l408 -408q28 -28 28 -68q0 -13 -4.5 -26t-9 -22t-15.5 -22t-16.5 -18.5t-20.5 -19t-18 -16.5q-30 -28 -68 -28 q-10 0 -18 1.5t-16.5 5.5t-13.5 6t-13.5 10t-11.5 10t-13 12.5t-12 12.5q14 -14 14 -34t-14 -34l-126 -126l256 -256q43 43 96 43q52 0 91 -37l363 -363q37 -39 37 -91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM576 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1004 351l101 382q6 26 -7.5 48.5t-38.5 29.5 t-48 -6.5t-30 -39.5l-101 -382q-60 -5 -107 -43.5t-63 -98.5q-20 -77 20 -146t117 -89t146 20t89 117q16 60 -6 117t-72 91zM1664 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1024 1024q0 53 -37.5 90.5 t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1472 832q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1792 384q0 -261 -141 -483q-19 -29 -54 -29h-1402q-35 0 -54 29 q-141 221 -141 483q0 182 71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M896 1152q-204 0 -381.5 -69.5t-282 -187.5t-104.5 -255q0 -112 71.5 -213.5t201.5 -175.5l87 -50l-27 -96q-24 -91 -70 -172q152 63 275 171l43 38l57 -6q69 -8 130 -8q204 0 381.5 69.5t282 187.5t104.5 255t-104.5 255t-282 187.5t-381.5 69.5zM1792 640 q0 -174 -120 -321.5t-326 -233t-450 -85.5q-70 0 -145 8q-198 -175 -460 -242q-49 -14 -114 -22h-5q-15 0 -27 10.5t-16 27.5v1q-3 4 -0.5 12t2 10t4.5 9.5l6 9t7 8.5t8 9q7 8 31 34.5t34.5 38t31 39.5t32.5 51t27 59t26 76q-157 89 -247.5 220t-90.5 281q0 174 120 321.5 t326 233t450 85.5t450 -85.5t326 -233t120 -321.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M704 1152q-153 0 -286 -52t-211.5 -141t-78.5 -191q0 -82 53 -158t149 -132l97 -56l-35 -84q34 20 62 39l44 31l53 -10q78 -14 153 -14q153 0 286 52t211.5 141t78.5 191t-78.5 191t-211.5 141t-286 52zM704 1280q191 0 353.5 -68.5t256.5 -186.5t94 -257t-94 -257 t-256.5 -186.5t-353.5 -68.5q-86 0 -176 16q-124 -88 -278 -128q-36 -9 -86 -16h-3q-11 0 -20.5 8t-11.5 21q-1 3 -1 6.5t0.5 6.5t2 6l2.5 5t3.5 5.5t4 5t4.5 5t4 4.5q5 6 23 25t26 29.5t22.5 29t25 38.5t20.5 44q-124 72 -195 177t-71 224q0 139 94 257t256.5 186.5 t353.5 68.5zM1526 111q10 -24 20.5 -44t25 -38.5t22.5 -29t26 -29.5t23 -25q1 -1 4 -4.5t4.5 -5t4 -5t3.5 -5.5l2.5 -5t2 -6t0.5 -6.5t-1 -6.5q-3 -14 -13 -22t-22 -7q-50 7 -86 16q-154 40 -278 128q-90 -16 -176 -16q-271 0 -472 132q58 -4 88 -4q161 0 309 45t264 129 q125 92 192 212t67 254q0 77 -23 152q129 -71 204 -178t75 -230q0 -120 -71 -224.5t-195 -176.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="896" d="M885 970q18 -20 7 -44l-540 -1157q-13 -25 -42 -25q-4 0 -14 2q-17 5 -25.5 19t-4.5 30l197 808l-406 -101q-4 -1 -12 -1q-18 0 -31 11q-18 15 -13 39l201 825q4 14 16 23t28 9h328q19 0 32 -12.5t13 -29.5q0 -8 -5 -18l-171 -463l396 98q8 2 12 2q19 0 34 -15z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 288v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192h-512v-192h96q40 0 68 -28t28 -68v-320 q0 -40 -28 -68t-68 -28h-320q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h96v192q0 52 38 90t90 38h512v192h-96q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h320q40 0 68 -28t28 -68v-320q0 -40 -28 -68t-68 -28h-96v-192h512q52 0 90 -38t38 -90v-192h96q40 0 68 -28t28 -68 z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M896 708v-580q0 -104 -76 -180t-180 -76t-180 76t-76 180q0 26 19 45t45 19t45 -19t19 -45q0 -50 39 -89t89 -39t89 39t39 89v580q33 11 64 11t64 -11zM1664 681q0 -13 -9.5 -22.5t-22.5 -9.5q-11 0 -23 10q-49 46 -93 69t-102 23q-68 0 -128 -37t-103 -97 q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -28 -17q-18 0 -29 17q-4 6 -14.5 24t-17.5 28q-43 60 -102.5 97t-127.5 37t-127.5 -37t-102.5 -97q-7 -10 -17.5 -28t-14.5 -24q-11 -17 -29 -17q-17 0 -28 17q-4 6 -14.5 24t-17.5 28q-43 60 -103 97t-128 37q-58 0 -102 -23t-93 -69 q-12 -10 -23 -10q-13 0 -22.5 9.5t-9.5 22.5q0 5 1 7q45 183 172.5 319.5t298 204.5t360.5 68q140 0 274.5 -40t246.5 -113.5t194.5 -187t115.5 -251.5q1 -2 1 -7zM896 1408v-98q-42 2 -64 2t-64 -2v98q0 26 19 45t45 19t45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M768 -128h896v640h-416q-40 0 -68 28t-28 68v416h-384v-1152zM1024 1312v64q0 13 -9.5 22.5t-22.5 9.5h-704q-13 0 -22.5 -9.5t-9.5 -22.5v-64q0 -13 9.5 -22.5t22.5 -9.5h704q13 0 22.5 9.5t9.5 22.5zM1280 640h299l-299 299v-299zM1792 512v-672q0 -40 -28 -68t-68 -28 h-960q-40 0 -68 28t-28 68v160h-544q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h1088q40 0 68 -28t28 -68v-328q21 -13 36 -28l408 -408q28 -28 48 -76t20 -88z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M736 960q0 -13 -9.5 -22.5t-22.5 -9.5t-22.5 9.5t-9.5 22.5q0 46 -54 71t-106 25q-13 0 -22.5 9.5t-9.5 22.5t9.5 22.5t22.5 9.5q50 0 99.5 -16t87 -54t37.5 -90zM896 960q0 72 -34.5 134t-90 101.5t-123 62t-136.5 22.5t-136.5 -22.5t-123 -62t-90 -101.5t-34.5 -134 q0 -101 68 -180q10 -11 30.5 -33t30.5 -33q128 -153 141 -298h228q13 145 141 298q10 11 30.5 33t30.5 33q68 79 68 180zM1024 960q0 -155 -103 -268q-45 -49 -74.5 -87t-59.5 -95.5t-34 -107.5q47 -28 47 -82q0 -37 -25 -64q25 -27 25 -64q0 -52 -45 -81q13 -23 13 -47 q0 -46 -31.5 -71t-77.5 -25q-20 -44 -60 -70t-87 -26t-87 26t-60 70q-46 0 -77.5 25t-31.5 71q0 24 13 47q-45 29 -45 81q0 37 25 64q-25 27 -25 64q0 54 47 82q-4 50 -34 107.5t-59.5 95.5t-74.5 87q-103 113 -103 268q0 99 44.5 184.5t117 142t164 89t186.5 32.5 t186.5 -32.5t164 -89t117 -142t44.5 -184.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 352v-192q0 -13 -9.5 -22.5t-22.5 -9.5h-1376v-192q0 -13 -9.5 -22.5t-22.5 -9.5q-12 0 -24 10l-319 320q-9 9 -9 22q0 14 9 23l320 320q9 9 23 9q13 0 22.5 -9.5t9.5 -22.5v-192h1376q13 0 22.5 -9.5t9.5 -22.5zM1792 896q0 -14 -9 -23l-320 -320q-9 -9 -23 -9 q-13 0 -22.5 9.5t-9.5 22.5v192h-1376q-13 0 -22.5 9.5t-9.5 22.5v192q0 13 9.5 22.5t22.5 9.5h1376v192q0 14 9 23t23 9q12 0 24 -10l319 -319q9 -9 9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 608q0 14 -9 23t-23 9h-224v352q0 13 -9.5 22.5t-22.5 9.5h-192q-13 0 -22.5 -9.5t-9.5 -22.5v-352h-224q-13 0 -22.5 -9.5t-9.5 -22.5q0 -14 9 -23l352 -352q9 -9 23 -9t23 9l351 351q10 12 10 24zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1280 672q0 14 -9 23l-352 352q-9 9 -23 9t-23 -9l-351 -351q-10 -12 -10 -24q0 -14 9 -23t23 -9h224v-352q0 -13 9.5 -22.5t22.5 -9.5h192q13 0 22.5 9.5t9.5 22.5v352h224q13 0 22.5 9.5t9.5 22.5zM1920 384q0 -159 -112.5 -271.5t-271.5 -112.5h-1088 q-185 0 -316.5 131.5t-131.5 316.5q0 130 70 240t188 165q-2 30 -2 43q0 212 150 362t362 150q156 0 285.5 -87t188.5 -231q71 62 166 62q106 0 181 -75t75 -181q0 -76 -41 -138q130 -31 213.5 -135.5t83.5 -238.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM1408 131q0 -121 -73 -190t-194 -69h-874q-121 0 -194 69t-73 190q0 68 5.5 131t24 138t47.5 132.5t81 103t120 60.5q-22 -52 -22 -120v-203q-58 -20 -93 -70t-35 -111q0 -80 56 -136t136 -56 t136 56t56 136q0 61 -35.5 111t-92.5 70v203q0 62 25 93q132 -104 295 -104t295 104q25 -31 25 -93v-64q-106 0 -181 -75t-75 -181v-89q-32 -29 -32 -71q0 -40 28 -68t68 -28t68 28t28 68q0 42 -32 71v89q0 52 38 90t90 38t90 -38t38 -90v-89q-32 -29 -32 -71q0 -40 28 -68 t68 -28t68 28t28 68q0 42 -32 71v89q0 68 -34.5 127.5t-93.5 93.5q0 10 0.5 42.5t0 48t-2.5 41.5t-7 47t-13 40q68 -15 120 -60.5t81 -103t47.5 -132.5t24 -138t5.5 -131zM1088 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1280 832q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 832q0 -62 -35.5 -111t-92.5 -70v-395q0 -159 -131.5 -271.5t-316.5 -112.5t-316.5 112.5t-131.5 271.5v132q-164 20 -274 128t-110 252v512q0 26 19 45t45 19q6 0 16 -2q17 30 47 48 t65 18q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5q-33 0 -64 18v-402q0 -106 94 -181t226 -75t226 75t94 181v402q-31 -18 -64 -18q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5q35 0 65 -18t47 -48q10 2 16 2q26 0 45 -19t19 -45v-512q0 -144 -110 -252 t-274 -128v-132q0 -106 94 -181t226 -75t226 75t94 181v395q-57 21 -92.5 70t-35.5 111q0 80 56 136t136 56t136 -56t56 -136z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M640 1152h512v128h-512v-128zM288 1152v-1280h-64q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h64zM1408 1152v-1280h-1024v1280h128v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h128zM1792 928v-832q0 -92 -66 -158t-158 -66h-64v1280h64q92 0 158 -66 t66 -158z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M848 -160q0 16 -16 16q-59 0 -101.5 42.5t-42.5 101.5q0 16 -16 16t-16 -16q0 -73 51.5 -124.5t124.5 -51.5q16 0 16 16zM1664 128q0 -52 -38 -90t-90 -38h-448q0 -106 -75 -181t-181 -75t-181 75t-75 181h-448q-52 0 -90 38t-38 90q190 161 287 397.5t97 498.5 q0 165 96 262t264 117q-8 18 -8 37q0 40 28 68t68 28t68 -28t28 -68q0 -19 -8 -37q168 -20 264 -117t96 -262q0 -262 97 -498.5t287 -397.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1664 896q0 80 -56 136t-136 56h-64v-384h64q80 0 136 56t56 136zM0 128h1792q0 -106 -75 -181t-181 -75h-1280q-106 0 -181 75t-75 181zM1856 896q0 -159 -112.5 -271.5t-271.5 -112.5h-64v-32q0 -92 -66 -158t-158 -66h-704q-92 0 -158 66t-66 158v736q0 26 19 45 t45 19h1152q159 0 271.5 -112.5t112.5 -271.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M640 1472v-640q0 -61 -35.5 -111t-92.5 -70v-779q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v779q-57 20 -92.5 70t-35.5 111v640q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45v-416q0 -26 19 -45 t45 -19t45 19t19 45v416q0 26 19 45t45 19t45 -19t19 -45zM1408 1472v-1600q0 -52 -38 -90t-90 -38h-128q-52 0 -90 38t-38 90v512h-224q-13 0 -22.5 9.5t-9.5 22.5v800q0 132 94 226t226 94h256q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1024 352v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM1024 608v-64q0 -14 -9 -23t-23 -9h-704q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h704q14 0 23 -9t9 -23zM128 0h1024v768h-416q-40 0 -68 28t-28 68v416h-512v-1280z M768 896h376q-10 29 -22 41l-313 313q-12 12 -41 22v-376zM1280 864v-896q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h640q40 0 88 -20t76 -48l312 -312q28 -28 48 -76t20 -88z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 992v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 1248v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1536h-1152v-1536h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM1408 1472v-1664q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1664q0 26 19 45t45 19h1280q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM384 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M1152 224v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM896 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M640 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 480v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5zM1152 736v-64q0 -13 -9.5 -22.5t-22.5 -9.5h-64q-13 0 -22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h64q13 0 22.5 -9.5t9.5 -22.5z M896 -128h384v1152h-256v-32q0 -40 -28 -68t-68 -28h-448q-40 0 -68 28t-28 68v32h-256v-1152h384v224q0 13 9.5 22.5t22.5 9.5h320q13 0 22.5 -9.5t9.5 -22.5v-224zM896 1056v320q0 13 -9.5 22.5t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-96h-128v96q0 13 -9.5 22.5 t-22.5 9.5h-64q-13 0 -22.5 -9.5t-9.5 -22.5v-320q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5v96h128v-96q0 -13 9.5 -22.5t22.5 -9.5h64q13 0 22.5 9.5t9.5 22.5zM1408 1088v-1280q0 -26 -19 -45t-45 -19h-1280q-26 0 -45 19t-19 45v1280q0 26 19 45t45 19h320 v288q0 40 28 68t68 28h448q40 0 68 -28t28 -68v-288h320q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M640 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM256 640h384v256h-158q-14 -2 -22 -9l-195 -195q-7 -12 -9 -22v-30zM1536 128q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5zM1664 800v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM1920 1344v-1152 q0 -26 -19 -45t-45 -19h-192q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-128q-26 0 -45 19t-19 45t19 45t45 19v416q0 26 13 58t32 51l198 198q19 19 51 32t58 13h160v320q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 416v192q0 14 -9 23t-23 9h-224v224q0 14 -9 23t-23 9h-192q-14 0 -23 -9t-9 -23v-224h-224q-14 0 -23 -9t-9 -23v-192q0 -14 9 -23t23 -9h224v-224q0 -14 9 -23t23 -9h192q14 0 23 9t9 23v224h224q14 0 23 9t9 23zM640 1152h512v128h-512v-128zM256 1152v-1280h-32 q-92 0 -158 66t-66 158v832q0 92 66 158t158 66h32zM1440 1152v-1280h-1088v1280h160v160q0 40 28 68t68 28h576q40 0 68 -28t28 -68v-160h160zM1792 928v-832q0 -92 -66 -158t-158 -66h-32v1280h32q92 0 158 -66t66 -158z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1920 576q-1 -32 -288 -96l-352 -32l-224 -64h-64l-293 -352h69q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-96h-160h-64v32h64v416h-160l-192 -224h-96l-32 32v192h32v32h128v8l-192 24v128l192 24v8h-128v32h-32v192l32 32h96l192 -224h160v416h-64v32h64h160h96 q26 0 45 -4.5t19 -11.5t-19 -11.5t-45 -4.5h-69l293 -352h64l224 -64l352 -32q261 -58 287 -93z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M640 640v384h-256v-256q0 -53 37.5 -90.5t90.5 -37.5h128zM1664 192v-192h-1152v192l128 192h-128q-159 0 -271.5 112.5t-112.5 271.5v320l-64 64l32 128h480l32 128h960l32 -192l-64 -32v-800z" />
|
|
||||||
<glyph unicode="" d="M1280 192v896q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-512v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-896q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h512v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-320v320q0 26 -19 45t-45 19h-128q-26 0 -45 -19t-19 -45v-320h-320q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h320v-320q0 -26 19 -45t45 -19h128q26 0 45 19t19 45v320h320q26 0 45 19t19 45zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M627 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23zM1011 160q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM979 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23 l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 224q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23zM1075 608q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393 q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 672q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23zM1075 1056q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23 t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M627 992q0 -13 -10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M595 576q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 352q0 -13 -10 -23l-50 -50q-10 -10 -23 -10t-23 10l-393 393l-393 -393q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l466 -466q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1075 800q0 -13 -10 -23l-466 -466q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l393 -393l393 393q10 10 23 10t23 -10l50 -50q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1792 544v832q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-832q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1376v-1088q0 -66 -47 -113t-113 -47h-544q0 -37 16 -77.5t32 -71t16 -43.5q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19 t-19 45q0 14 16 44t32 70t16 78h-544q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M416 256q-66 0 -113 47t-47 113v704q0 66 47 113t113 47h1088q66 0 113 -47t47 -113v-704q0 -66 -47 -113t-113 -47h-1088zM384 1120v-704q0 -13 9.5 -22.5t22.5 -9.5h1088q13 0 22.5 9.5t9.5 22.5v704q0 13 -9.5 22.5t-22.5 9.5h-1088q-13 0 -22.5 -9.5t-9.5 -22.5z M1760 192h160v-96q0 -40 -47 -68t-113 -28h-1600q-66 0 -113 28t-47 68v96h160h1600zM1040 96q16 0 16 16t-16 16h-160q-16 0 -16 -16t16 -16h160z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M640 128q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1024 288v960q0 13 -9.5 22.5t-22.5 9.5h-832q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h832q13 0 22.5 9.5t9.5 22.5zM1152 1248v-1088q0 -66 -47 -113t-113 -47h-832 q-66 0 -113 47t-47 113v1088q0 66 47 113t113 47h832q66 0 113 -47t47 -113z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M464 128q0 33 -23.5 56.5t-56.5 23.5t-56.5 -23.5t-23.5 -56.5t23.5 -56.5t56.5 -23.5t56.5 23.5t23.5 56.5zM672 288v704q0 13 -9.5 22.5t-22.5 9.5h-512q-13 0 -22.5 -9.5t-9.5 -22.5v-704q0 -13 9.5 -22.5t22.5 -9.5h512q13 0 22.5 9.5t9.5 22.5zM480 1136 q0 16 -16 16h-160q-16 0 -16 -16t16 -16h160q16 0 16 16zM768 1152v-1024q0 -52 -38 -90t-90 -38h-512q-52 0 -90 38t-38 90v1024q0 52 38 90t90 38h512q52 0 90 -38t38 -90z" />
|
|
||||||
<glyph unicode="" d="M768 1184q-148 0 -273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273t-73 273t-198 198t-273 73zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103 t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M768 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z M1664 576v-384q0 -80 -56 -136t-136 -56h-384q-80 0 -136 56t-56 136v704q0 104 40.5 198.5t109.5 163.5t163.5 109.5t198.5 40.5h64q26 0 45 -19t19 -45v-128q0 -26 -19 -45t-45 -19h-64q-106 0 -181 -75t-75 -181v-32q0 -40 28 -68t68 -28h224q80 0 136 -56t56 -136z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M768 1216v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136zM1664 1216 v-704q0 -104 -40.5 -198.5t-109.5 -163.5t-163.5 -109.5t-198.5 -40.5h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64q106 0 181 75t75 181v32q0 40 -28 68t-68 28h-224q-80 0 -136 56t-56 136v384q0 80 56 136t136 56h384q80 0 136 -56t56 -136z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1568" d="M496 192q0 -60 -42.5 -102t-101.5 -42q-60 0 -102 42t-42 102t42 102t102 42q59 0 101.5 -42t42.5 -102zM928 0q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM320 640q0 -66 -47 -113t-113 -47t-113 47t-47 113 t47 113t113 47t113 -47t47 -113zM1360 192q0 -46 -33 -79t-79 -33t-79 33t-33 79t33 79t79 33t79 -33t33 -79zM528 1088q0 -73 -51.5 -124.5t-124.5 -51.5t-124.5 51.5t-51.5 124.5t51.5 124.5t124.5 51.5t124.5 -51.5t51.5 -124.5zM992 1280q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1536 640q0 -40 -28 -68t-68 -28t-68 28t-28 68t28 68t68 28t68 -28t28 -68zM1328 1088q0 -33 -23.5 -56.5t-56.5 -23.5t-56.5 23.5t-23.5 56.5t23.5 56.5t56.5 23.5t56.5 -23.5t23.5 -56.5z" />
|
|
||||||
<glyph unicode="" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 416q0 -166 -127 -451q-3 -7 -10.5 -24t-13.5 -30t-13 -22q-12 -17 -28 -17q-15 0 -23.5 10t-8.5 25q0 9 2.5 26.5t2.5 23.5q5 68 5 123q0 101 -17.5 181t-48.5 138.5t-80 101t-105.5 69.5t-133 42.5t-154 21.5t-175.5 6h-224v-256q0 -26 -19 -45t-45 -19t-45 19 l-512 512q-19 19 -19 45t19 45l512 512q19 19 45 19t45 -19t19 -45v-256h224q713 0 875 -403q53 -134 53 -333z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M640 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1280 320q0 -40 -12.5 -82t-43 -76t-72.5 -34t-72.5 34t-43 76t-12.5 82t12.5 82t43 76t72.5 34t72.5 -34t43 -76t12.5 -82zM1440 320 q0 120 -69 204t-187 84q-41 0 -195 -21q-71 -11 -157 -11t-157 11q-152 21 -195 21q-118 0 -187 -84t-69 -204q0 -88 32 -153.5t81 -103t122 -60t140 -29.5t149 -7h168q82 0 149 7t140 29.5t122 60t81 103t32 153.5zM1664 496q0 -207 -61 -331q-38 -77 -105.5 -133t-141 -86 t-170 -47.5t-171.5 -22t-167 -4.5q-78 0 -142 3t-147.5 12.5t-152.5 30t-137 51.5t-121 81t-86 115q-62 123 -62 331q0 237 136 396q-27 82 -27 170q0 116 51 218q108 0 190 -39.5t189 -123.5q147 35 309 35q148 0 280 -32q105 82 187 121t189 39q51 -102 51 -218 q0 -87 -27 -168q136 -160 136 -398z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1536 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68v-960q0 -40 28 -68t68 -28h1216q40 0 68 28t28 68zM1664 928v-704q0 -92 -66 -158t-158 -66h-1216q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320 q92 0 158 -66t66 -158v-32h672q92 0 158 -66t66 -158z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1781 605q0 35 -53 35h-1088q-40 0 -85.5 -21.5t-71.5 -52.5l-294 -363q-18 -24 -18 -40q0 -35 53 -35h1088q40 0 86 22t71 53l294 363q18 22 18 39zM640 768h768v160q0 40 -28 68t-68 28h-576q-40 0 -68 28t-28 68v64q0 40 -28 68t-68 28h-320q-40 0 -68 -28t-28 -68 v-853l256 315q44 53 116 87.5t140 34.5zM1909 605q0 -62 -46 -120l-295 -363q-43 -53 -116 -87.5t-140 -34.5h-1088q-92 0 -158 66t-66 158v960q0 92 66 158t158 66h320q92 0 158 -66t66 -158v-32h544q92 0 158 -66t66 -158v-160h192q54 0 99 -24.5t67 -70.5q15 -32 15 -68z " />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M896 608v-64q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224h224q14 0 23 -9t9 -23zM1024 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 -28 t-28 -68v-704q0 -40 28 -68t68 -28h704q40 0 68 28t28 68zM1152 928v-704q0 -92 -65.5 -158t-158.5 -66h-704q-93 0 -158.5 66t-65.5 158v704q0 93 65.5 158.5t158.5 65.5h704q93 0 158.5 -65.5t65.5 -158.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M928 1152q93 0 158.5 -65.5t65.5 -158.5v-704q0 -92 -65.5 -158t-158.5 -66h-704q-93 0 -158.5 66t-65.5 158v704q0 93 65.5 158.5t158.5 65.5h704zM1024 224v704q0 40 -28 68t-68 28h-704q-40 0 -68 -28t-28 -68v-704q0 -40 28 -68t68 -28h704q40 0 68 28t28 68z M864 640q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576z" />
|
|
||||||
<glyph unicode="" d="M1134 461q-37 -121 -138 -195t-228 -74t-228 74t-138 195q-8 25 4 48.5t38 31.5q25 8 48.5 -4t31.5 -38q25 -80 92.5 -129.5t151.5 -49.5t151.5 49.5t92.5 129.5q8 26 32 38t49 4t37 -31.5t4 -48.5zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5 t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5 t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1134 307q8 -25 -4 -48.5t-37 -31.5t-49 4t-32 38q-25 80 -92.5 129.5t-151.5 49.5t-151.5 -49.5t-92.5 -129.5q-8 -26 -31.5 -38t-48.5 -4q-26 8 -38 31.5t-4 48.5q37 121 138 195t228 74t228 -74t138 -195zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204 t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1152 448q0 -26 -19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h640q26 0 45 -19t19 -45zM640 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1152 896q0 -53 -37.5 -90.5t-90.5 -37.5t-90.5 37.5 t-37.5 90.5t37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M832 448v128q0 14 -9 23t-23 9h-192v192q0 14 -9 23t-23 9h-128q-14 0 -23 -9t-9 -23v-192h-192q-14 0 -23 -9t-9 -23v-128q0 -14 9 -23t23 -9h192v-192q0 -14 9 -23t23 -9h128q14 0 23 9t9 23v192h192q14 0 23 9t9 23zM1408 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 640q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1920 512q0 -212 -150 -362t-362 -150q-192 0 -338 128h-220q-146 -128 -338 -128q-212 0 -362 150 t-150 362t150 362t362 150h896q212 0 362 -150t150 -362z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M384 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM512 624v-96q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h224q16 0 16 -16zM384 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 368v-96q0 -16 -16 -16 h-864q-16 0 -16 16v96q0 16 16 16h864q16 0 16 -16zM768 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM640 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1024 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16 h96q16 0 16 -16zM896 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1280 624v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 368v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1152 880v-96 q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1408 880v-96q0 -16 -16 -16h-96q-16 0 -16 16v96q0 16 16 16h96q16 0 16 -16zM1664 880v-352q0 -16 -16 -16h-224q-16 0 -16 16v96q0 16 16 16h112v240q0 16 16 16h96q16 0 16 -16zM1792 128v896h-1664v-896 h1664zM1920 1024v-896q0 -53 -37.5 -90.5t-90.5 -37.5h-1664q-53 0 -90.5 37.5t-37.5 90.5v896q0 53 37.5 90.5t90.5 37.5h1664q53 0 90.5 -37.5t37.5 -90.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1664 491v616q-169 -91 -306 -91q-82 0 -145 32q-100 49 -184 76.5t-178 27.5q-173 0 -403 -127v-599q245 113 433 113q55 0 103.5 -7.5t98 -26t77 -31t82.5 -39.5l28 -14q44 -22 101 -22q120 0 293 92zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9 h-64q-14 0 -23 9t-9 23v1266q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102 q-15 -9 -33 -9q-16 0 -32 8q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M832 536v192q-181 -16 -384 -117v-185q205 96 384 110zM832 954v197q-172 -8 -384 -126v-189q215 111 384 118zM1664 491v184q-235 -116 -384 -71v224q-20 6 -39 15q-5 3 -33 17t-34.5 17t-31.5 15t-34.5 15.5t-32.5 13t-36 12.5t-35 8.5t-39.5 7.5t-39.5 4t-44 2 q-23 0 -49 -3v-222h19q102 0 192.5 -29t197.5 -82q19 -9 39 -15v-188q42 -17 91 -17q120 0 293 92zM1664 918v189q-169 -91 -306 -91q-45 0 -78 8v-196q148 -42 384 90zM320 1280q0 -35 -17.5 -64t-46.5 -46v-1266q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v1266 q-29 17 -46.5 46t-17.5 64q0 53 37.5 90.5t90.5 37.5t90.5 -37.5t37.5 -90.5zM1792 1216v-763q0 -39 -35 -57q-10 -5 -17 -9q-218 -116 -369 -116q-88 0 -158 35l-28 14q-64 33 -99 48t-91 29t-114 14q-102 0 -235.5 -44t-228.5 -102q-15 -9 -33 -9q-16 0 -32 8 q-32 19 -32 56v742q0 35 31 55q35 21 78.5 42.5t114 52t152.5 49.5t155 19q112 0 209 -31t209 -86q38 -19 89 -19q122 0 310 112q22 12 31 17q31 16 62 -2q31 -20 31 -55z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M585 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23zM1664 96v-64q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h960q14 0 23 -9 t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M617 137l-50 -50q-10 -10 -23 -10t-23 10l-466 466q-10 10 -10 23t10 23l466 466q10 10 23 10t23 -10l50 -50q10 -10 10 -23t-10 -23l-393 -393l393 -393q10 -10 10 -23t-10 -23zM1208 1204l-373 -1291q-4 -13 -15.5 -19.5t-23.5 -2.5l-62 17q-13 4 -19.5 15.5t-2.5 24.5 l373 1291q4 13 15.5 19.5t23.5 2.5l62 -17q13 -4 19.5 -15.5t2.5 -24.5zM1865 553l-466 -466q-10 -10 -23 -10t-23 10l-50 50q-10 10 -10 23t10 23l393 393l-393 393q-10 10 -10 23t10 23l50 50q10 10 23 10t23 -10l466 -466q10 -10 10 -23t-10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M640 454v-70q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-69l-397 -398q-19 -19 -19 -45t19 -45zM1792 416q0 -58 -17 -133.5t-38.5 -138t-48 -125t-40.5 -90.5l-20 -40q-8 -17 -28 -17q-6 0 -9 1 q-25 8 -23 34q43 400 -106 565q-64 71 -170.5 110.5t-267.5 52.5v-251q0 -42 -39 -59q-13 -5 -25 -5q-27 0 -45 19l-512 512q-19 19 -19 45t19 45l512 512q29 31 70 14q39 -17 39 -59v-262q411 -28 599 -221q169 -173 169 -509z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1186 579l257 250l-356 52l-66 10l-30 60l-159 322v-963l59 -31l318 -168l-60 355l-12 66zM1638 841l-363 -354l86 -500q5 -33 -6 -51.5t-34 -18.5q-17 0 -40 12l-449 236l-449 -236q-23 -12 -40 -12q-23 0 -34 18.5t-6 51.5l86 500l-364 354q-32 32 -23 59.5t54 34.5 l502 73l225 455q20 41 49 41q28 0 49 -41l225 -455l502 -73q45 -7 54 -34.5t-24 -59.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1401 1187l-640 -1280q-17 -35 -57 -35q-5 0 -15 2q-22 5 -35.5 22.5t-13.5 39.5v576h-576q-22 0 -39.5 13.5t-22.5 35.5t4 42t29 30l1280 640q13 7 29 7q27 0 45 -19q15 -14 18.5 -34.5t-6.5 -39.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M557 256h595v595zM512 301l595 595h-595v-595zM1664 224v-192q0 -14 -9 -23t-23 -9h-224v-224q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v224h-864q-14 0 -23 9t-9 23v864h-224q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h224v224q0 14 9 23t23 9h192q14 0 23 -9t9 -23 v-224h851l246 247q10 9 23 9t23 -9q9 -10 9 -23t-9 -23l-247 -246v-851h224q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M288 64q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM288 1216q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM928 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1024 1088q0 -52 -26 -96.5t-70 -69.5 q-2 -287 -226 -414q-68 -38 -203 -81q-128 -40 -169.5 -71t-41.5 -100v-26q44 -25 70 -69.5t26 -96.5q0 -80 -56 -136t-136 -56t-136 56t-56 136q0 52 26 96.5t70 69.5v820q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136q0 -52 -26 -96.5t-70 -69.5v-497 q54 26 154 57q55 17 87.5 29.5t70.5 31t59 39.5t40.5 51t28 69.5t8.5 91.5q-44 25 -70 69.5t-26 96.5q0 80 56 136t136 56t136 -56t56 -136z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M439 265l-256 -256q-10 -9 -23 -9q-12 0 -23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23zM608 224v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM384 448q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23t9 23t23 9h320 q14 0 23 -9t9 -23zM1648 320q0 -120 -85 -203l-147 -146q-83 -83 -203 -83q-121 0 -204 85l-334 335q-21 21 -42 56l239 18l273 -274q27 -27 68 -27.5t68 26.5l147 146q28 28 28 67q0 40 -28 68l-274 275l18 239q35 -21 56 -42l336 -336q84 -86 84 -204zM1031 1044l-239 -18 l-273 274q-28 28 -68 28q-39 0 -68 -27l-147 -146q-28 -28 -28 -67q0 -40 28 -68l274 -274l-18 -240q-35 21 -56 42l-336 336q-84 86 -84 204q0 120 85 203l147 146q83 83 203 83q121 0 204 -85l334 -335q21 -21 42 -56zM1664 960q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9 t-9 23t9 23t23 9h320q14 0 23 -9t9 -23zM1120 1504v-320q0 -14 -9 -23t-23 -9t-23 9t-9 23v320q0 14 9 23t23 9t23 -9t9 -23zM1527 1353l-256 -256q-11 -9 -23 -9t-23 9q-9 10 -9 23t9 23l256 256q10 9 23 9t23 -9q9 -10 9 -23t-9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M704 280v-240q0 -16 -12 -28t-28 -12h-240q-16 0 -28 12t-12 28v240q0 16 12 28t28 12h240q16 0 28 -12t12 -28zM1020 880q0 -54 -15.5 -101t-35 -76.5t-55 -59.5t-57.5 -43.5t-61 -35.5q-41 -23 -68.5 -65t-27.5 -67q0 -17 -12 -32.5t-28 -15.5h-240q-15 0 -25.5 18.5 t-10.5 37.5v45q0 83 65 156.5t143 108.5q59 27 84 56t25 76q0 42 -46.5 74t-107.5 32q-65 0 -108 -29q-35 -25 -107 -115q-13 -16 -31 -16q-12 0 -25 8l-164 125q-13 10 -15.5 25t5.5 28q160 266 464 266q80 0 161 -31t146 -83t106 -127.5t41 -158.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M640 192v-128q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h64v384h-64q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h384q26 0 45 -19t19 -45v-576h64q26 0 45 -19t19 -45zM512 1344v-192q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v192 q0 26 19 45t45 19h256q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="640" d="M512 288v-224q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v224q0 26 19 45t45 19h256q26 0 45 -19t19 -45zM542 1344l-28 -768q-1 -26 -20.5 -45t-45.5 -19h-256q-26 0 -45.5 19t-20.5 45l-28 768q-1 26 17.5 45t44.5 19h320q26 0 44.5 -19t17.5 -45z" />
|
|
||||||
<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1534 846v-206h-514l-3 27 q-4 28 -4 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q83 65 188 65q110 0 178 -59.5t68 -158.5q0 -56 -24.5 -103t-62 -76.5t-81.5 -58.5t-82 -50.5t-65.5 -51.5t-30.5 -63h232v80 h126z" />
|
|
||||||
<glyph unicode="" d="M897 167v-167h-248l-159 252l-24 42q-8 9 -11 21h-3l-9 -21q-10 -20 -25 -44l-155 -250h-258v167h128l197 291l-185 272h-137v168h276l139 -228q2 -4 23 -42q8 -9 11 -21h3q3 9 11 21l25 42l140 228h257v-168h-125l-184 -267l204 -296h109zM1536 -50v-206h-514l-4 27 q-3 45 -3 46q0 64 26 117t65 86.5t84 65t84 54.5t65 54t26 64q0 38 -29.5 62.5t-70.5 24.5q-51 0 -97 -39q-14 -11 -36 -38l-105 92q26 37 63 66q80 65 188 65q110 0 178 -59.5t68 -158.5q0 -66 -34.5 -118.5t-84 -86t-99.5 -62.5t-87 -63t-41 -73h232v80h126z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M896 128l336 384h-768l-336 -384h768zM1909 1205q15 -34 9.5 -71.5t-30.5 -65.5l-896 -1024q-38 -44 -96 -44h-768q-38 0 -69.5 20.5t-47.5 54.5q-15 34 -9.5 71.5t30.5 65.5l896 1024q38 44 96 44h768q38 0 69.5 -20.5t47.5 -54.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 438q0 -81 -44.5 -135t-123.5 -54q-41 0 -77.5 17.5t-59 38t-56.5 38t-71 17.5q-110 0 -110 -124q0 -39 16 -115t15 -115v-5q-22 0 -33 -1q-34 -3 -97.5 -11.5t-115.5 -13.5t-98 -5q-61 0 -103 26.5t-42 83.5q0 37 17.5 71t38 56.5t38 59t17.5 77.5q0 79 -54 123.5 t-135 44.5q-84 0 -143 -45.5t-59 -127.5q0 -43 15 -83t33.5 -64.5t33.5 -53t15 -50.5q0 -45 -46 -89q-37 -35 -117 -35q-95 0 -245 24q-9 2 -27.5 4t-27.5 4l-13 2q-1 0 -3 1q-2 0 -2 1v1024q2 -1 17.5 -3.5t34 -5t21.5 -3.5q150 -24 245 -24q80 0 117 35q46 44 46 89 q0 22 -15 50.5t-33.5 53t-33.5 64.5t-15 83q0 82 59 127.5t144 45.5q80 0 134 -44.5t54 -123.5q0 -41 -17.5 -77.5t-38 -59t-38 -56.5t-17.5 -71q0 -57 42 -83.5t103 -26.5q64 0 180 15t163 17v-2q-1 -2 -3.5 -17.5t-5 -34t-3.5 -21.5q-24 -150 -24 -245q0 -80 35 -117 q44 -46 89 -46q22 0 50.5 15t53 33.5t64.5 33.5t83 15q82 0 127.5 -59t45.5 -143z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1152 832v-128q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-217 24 -364.5 187.5t-147.5 384.5v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -185 131.5 -316.5t316.5 -131.5 t316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45zM896 1216v-512q0 -132 -94 -226t-226 -94t-226 94t-94 226v512q0 132 94 226t226 94t226 -94t94 -226z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M271 591l-101 -101q-42 103 -42 214v128q0 26 19 45t45 19t45 -19t19 -45v-128q0 -53 15 -113zM1385 1193l-361 -361v-128q0 -132 -94 -226t-226 -94q-55 0 -109 19l-96 -96q97 -51 205 -51q185 0 316.5 131.5t131.5 316.5v128q0 26 19 45t45 19t45 -19t19 -45v-128 q0 -221 -147.5 -384.5t-364.5 -187.5v-132h256q26 0 45 -19t19 -45t-19 -45t-45 -19h-640q-26 0 -45 19t-19 45t19 45t45 19h256v132q-125 13 -235 81l-254 -254q-10 -10 -23 -10t-23 10l-82 82q-10 10 -10 23t10 23l1234 1234q10 10 23 10t23 -10l82 -82q10 -10 10 -23 t-10 -23zM1005 1325l-621 -621v512q0 132 94 226t226 94q102 0 184.5 -59t116.5 -152z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1088 576v640h-448v-1137q119 63 213 137q235 184 235 360zM1280 1344v-768q0 -86 -33.5 -170.5t-83 -150t-118 -127.5t-126.5 -103t-121 -77.5t-89.5 -49.5t-42.5 -20q-12 -6 -26 -6t-26 6q-16 7 -42.5 20t-89.5 49.5t-121 77.5t-126.5 103t-118 127.5t-83 150 t-33.5 170.5v768q0 26 19 45t45 19h1152q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280 q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128q52 0 90 -38t38 -90z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M512 1344q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1408 1376v-320q0 -16 -12 -25q-8 -7 -20 -7q-4 0 -7 1l-448 96q-11 2 -18 11t-7 20h-256v-102q111 -23 183.5 -111t72.5 -203v-800q0 -26 -19 -45t-45 -19h-512q-26 0 -45 19t-19 45v800 q0 106 62.5 190.5t161.5 114.5v111h-32q-59 0 -115 -23.5t-91.5 -53t-66 -66.5t-40.5 -53.5t-14 -24.5q-17 -35 -57 -35q-16 0 -29 7q-23 12 -31.5 37t3.5 49q5 10 14.5 26t37.5 53.5t60.5 70t85 67t108.5 52.5q-25 42 -25 86q0 66 47 113t113 47t113 -47t47 -113 q0 -33 -14 -64h302q0 11 7 20t18 11l448 96q3 1 7 1q12 0 20 -7q12 -9 12 -25z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1440 1088q0 40 -28 68t-68 28t-68 -28t-28 -68t28 -68t68 -28t68 28t28 68zM1664 1376q0 -249 -75.5 -430.5t-253.5 -360.5q-81 -80 -195 -176l-20 -379q-2 -16 -16 -26l-384 -224q-7 -4 -16 -4q-12 0 -23 9l-64 64q-13 14 -8 32l85 276l-281 281l-276 -85q-3 -1 -9 -1 q-14 0 -23 9l-64 64q-17 19 -5 39l224 384q10 14 26 16l379 20q96 114 176 195q188 187 358 258t431 71q14 0 24 -9.5t10 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1708 881l-188 -881h-304l181 849q4 21 1 43q-4 20 -16 35q-10 14 -28 24q-18 9 -40 9h-197l-205 -960h-303l204 960h-304l-205 -960h-304l272 1280h1139q157 0 245 -118q86 -116 52 -281z" />
|
|
||||||
<glyph unicode="" d="M909 141l102 102q19 19 19 45t-19 45l-307 307l307 307q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M717 141l454 454q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l307 -307l-307 -307q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1165 397l102 102q19 19 19 45t-19 45l-454 454q-19 19 -45 19t-45 -19l-454 -454q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l307 307l307 -307q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M813 237l454 454q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-307 -307l-307 307q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l454 -454q19 -19 45 -19t45 19zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5 t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1130 939l16 175h-884l47 -534h612l-22 -228l-197 -53l-196 53l-13 140h-175l22 -278l362 -100h4v1l359 99l50 544h-644l-15 181h674zM0 1408h1408l-128 -1438l-578 -162l-574 162z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M275 1408h1505l-266 -1333l-804 -267l-698 267l71 356h297l-29 -147l422 -161l486 161l68 339h-1208l58 297h1209l38 191h-1208z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M960 1280q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1792 352v-352q0 -22 -20 -30q-8 -2 -12 -2q-13 0 -23 9l-93 93q-119 -143 -318.5 -226.5t-429.5 -83.5t-429.5 83.5t-318.5 226.5l-93 -93q-9 -9 -23 -9q-4 0 -12 2q-20 8 -20 30v352 q0 14 9 23t23 9h352q22 0 30 -20q8 -19 -7 -35l-100 -100q67 -91 189.5 -153.5t271.5 -82.5v647h-192q-26 0 -45 19t-19 45v128q0 26 19 45t45 19h192v163q-58 34 -93 92.5t-35 128.5q0 106 75 181t181 75t181 -75t75 -181q0 -70 -35 -128.5t-93 -92.5v-163h192q26 0 45 -19 t19 -45v-128q0 -26 -19 -45t-45 -19h-192v-647q149 20 271.5 82.5t189.5 153.5l-100 100q-15 16 -7 35q8 20 30 20h352q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1152" d="M1056 768q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h32v320q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45q0 106 -75 181t-181 75t-181 -75t-75 -181 v-320h736z" />
|
|
||||||
<glyph unicode="" d="M1024 640q0 -106 -75 -181t-181 -75t-181 75t-75 181t75 181t181 75t181 -75t75 -181zM1152 640q0 159 -112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5t271.5 -112.5t271.5 112.5t112.5 271.5zM1280 640q0 -212 -150 -362t-362 -150t-362 150 t-150 362t150 362t362 150t362 -150t150 -362zM1408 640q0 130 -51 248.5t-136.5 204t-204 136.5t-248.5 51t-248.5 -51t-204 -136.5t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5zM1536 640 q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM896 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM1408 800v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="384" d="M384 288v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 800v-192q0 -40 -28 -68t-68 -28h-192q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68zM384 1312v-192q0 -40 -28 -68t-68 -28h-192 q-40 0 -68 28t-28 68v192q0 40 28 68t68 28h192q40 0 68 -28t28 -68z" />
|
|
||||||
<glyph unicode="" d="M512 256q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM863 162q-13 232 -177 396t-396 177q-14 1 -24 -9t-10 -23v-128q0 -13 8.5 -22t21.5 -10q154 -11 264 -121t121 -264q1 -13 10 -21.5t22 -8.5h128q13 0 23 10 t9 24zM1247 161q-5 154 -56 297.5t-139.5 260t-205 205t-260 139.5t-297.5 56q-14 1 -23 -9q-10 -10 -10 -23v-128q0 -13 9 -22t22 -10q204 -7 378 -111.5t278.5 -278.5t111.5 -378q1 -13 10 -22t22 -9h128q13 0 23 10q11 9 9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM1152 585q32 18 32 55t-32 55l-544 320q-31 19 -64 1q-32 -19 -32 -56v-640q0 -37 32 -56 q16 -8 32 -8q17 0 32 9z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1024 1084l316 -316l-572 -572l-316 316zM813 105l618 618q19 19 19 45t-19 45l-362 362q-18 18 -45 18t-45 -18l-618 -618q-19 -19 -19 -45t19 -45l362 -362q18 -18 45 -18t45 18zM1702 742l-907 -908q-37 -37 -90.5 -37t-90.5 37l-126 126q56 56 56 136t-56 136 t-136 56t-136 -56l-125 126q-37 37 -37 90.5t37 90.5l907 906q37 37 90.5 37t90.5 -37l125 -125q-56 -56 -56 -136t56 -136t136 -56t136 56l126 -125q37 -37 37 -90.5t-37 -90.5z" />
|
|
||||||
<glyph unicode="" d="M1280 576v128q0 26 -19 45t-45 19h-896q-26 0 -45 -19t-19 -45v-128q0 -26 19 -45t45 -19h896q26 0 45 19t19 45zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1152 736v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h832q14 0 23 -9t9 -23zM1280 288v832q0 66 -47 113t-113 47h-832q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113zM1408 1120v-832q0 -119 -84.5 -203.5 t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h832q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1018 933q-18 -37 -58 -37h-192v-864q0 -14 -9 -23t-23 -9h-704q-21 0 -29 18q-8 20 4 35l160 192q9 11 25 11h320v640h-192q-40 0 -58 37q-17 37 9 68l320 384q18 22 49 22t49 -22l320 -384q27 -32 9 -68z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M32 1280h704q13 0 22.5 -9.5t9.5 -23.5v-863h192q40 0 58 -37t-9 -69l-320 -384q-18 -22 -49 -22t-49 22l-320 384q-26 31 -9 69q18 37 58 37h192v640h-320q-14 0 -25 11l-160 192q-13 14 -4 34q9 19 29 19z" />
|
|
||||||
<glyph unicode="" d="M685 237l614 614q19 19 19 45t-19 45l-102 102q-19 19 -45 19t-45 -19l-467 -467l-211 211q-19 19 -45 19t-45 -19l-102 -102q-19 -19 -19 -45t19 -45l358 -358q19 -19 45 -19t45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5 t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M404 428l152 -152l-52 -52h-56v96h-96v56zM818 818q14 -13 -3 -30l-291 -291q-17 -17 -30 -3q-14 13 3 30l291 291q17 17 30 3zM544 128l544 544l-288 288l-544 -544v-288h288zM1152 736l92 92q28 28 28 68t-28 68l-152 152q-28 28 -68 28t-68 -28l-92 -92zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1280 608v480q0 26 -19 45t-45 19h-480q-42 0 -59 -39q-17 -41 14 -70l144 -144l-534 -534q-19 -19 -19 -45t19 -45l102 -102q19 -19 45 -19t45 19l534 534l144 -144q18 -19 45 -19q12 0 25 5q39 17 39 59zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1005 435l352 352q19 19 19 45t-19 45l-352 352q-30 31 -69 14q-40 -17 -40 -59v-160q-119 0 -216 -19.5t-162.5 -51t-114 -79t-76.5 -95.5t-44.5 -109t-21.5 -111.5t-5 -110.5q0 -181 167 -404q10 -12 25 -12q7 0 13 3q22 9 19 33q-44 354 62 473q46 52 130 75.5 t224 23.5v-160q0 -42 40 -59q12 -5 24 -5q26 0 45 19zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M640 448l256 128l-256 128v-256zM1024 1039v-542l-512 -256v542zM1312 640q0 148 -73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273t73 -273t198 -198t273 -73t273 73t198 198t73 273zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1145 861q18 -35 -5 -66l-320 -448q-19 -27 -52 -27t-52 27l-320 448q-23 31 -5 66q17 35 57 35h640q40 0 57 -35zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1145 419q-17 -35 -57 -35h-640q-40 0 -57 35q-18 35 5 66l320 448q19 27 52 27t52 -27l320 -448q23 -31 5 -66zM1280 160v960q0 13 -9.5 22.5t-22.5 9.5h-960q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h960q13 0 22.5 9.5t9.5 22.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M1088 640q0 -33 -27 -52l-448 -320q-31 -23 -66 -5q-35 17 -35 57v640q0 40 35 57q35 18 66 -5l448 -320q27 -19 27 -52zM1280 160v960q0 14 -9 23t-23 9h-960q-14 0 -23 -9t-9 -23v-960q0 -14 9 -23t23 -9h960q14 0 23 9t9 23zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M976 229l35 -159q3 -12 -3 -22.5t-17 -14.5l-5 -1q-4 -2 -10.5 -3.5t-16 -4.5t-21.5 -5.5t-25.5 -5t-30 -5t-33.5 -4.5t-36.5 -3t-38.5 -1q-234 0 -409 130.5t-238 351.5h-95q-13 0 -22.5 9.5t-9.5 22.5v113q0 13 9.5 22.5t22.5 9.5h66q-2 57 1 105h-67q-14 0 -23 9 t-9 23v114q0 14 9 23t23 9h98q67 210 243.5 338t400.5 128q102 0 194 -23q11 -3 20 -15q6 -11 3 -24l-43 -159q-3 -13 -14 -19.5t-24 -2.5l-4 1q-4 1 -11.5 2.5l-17.5 3.5t-22.5 3.5t-26 3t-29 2.5t-29.5 1q-126 0 -226 -64t-150 -176h468q16 0 25 -12q10 -12 7 -26 l-24 -114q-5 -26 -32 -26h-488q-3 -37 0 -105h459q15 0 25 -12q9 -12 6 -27l-24 -112q-2 -11 -11 -18.5t-20 -7.5h-387q48 -117 149.5 -185.5t228.5 -68.5q18 0 36 1.5t33.5 3.5t29.5 4.5t24.5 5t18.5 4.5l12 3l5 2q13 5 26 -2q12 -7 15 -21z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1020 399v-367q0 -14 -9 -23t-23 -9h-956q-14 0 -23 9t-9 23v150q0 13 9.5 22.5t22.5 9.5h97v383h-95q-14 0 -23 9.5t-9 22.5v131q0 14 9 23t23 9h95v223q0 171 123.5 282t314.5 111q185 0 335 -125q9 -8 10 -20.5t-7 -22.5l-103 -127q-9 -11 -22 -12q-13 -2 -23 7 q-5 5 -26 19t-69 32t-93 18q-85 0 -137 -47t-52 -123v-215h305q13 0 22.5 -9t9.5 -23v-131q0 -13 -9.5 -22.5t-22.5 -9.5h-305v-379h414v181q0 13 9 22.5t23 9.5h162q14 0 23 -9.5t9 -22.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M978 351q0 -153 -99.5 -263.5t-258.5 -136.5v-175q0 -14 -9 -23t-23 -9h-135q-13 0 -22.5 9.5t-9.5 22.5v175q-66 9 -127.5 31t-101.5 44.5t-74 48t-46.5 37.5t-17.5 18q-17 21 -2 41l103 135q7 10 23 12q15 2 24 -9l2 -2q113 -99 243 -125q37 -8 74 -8q81 0 142.5 43 t61.5 122q0 28 -15 53t-33.5 42t-58.5 37.5t-66 32t-80 32.5q-39 16 -61.5 25t-61.5 26.5t-62.5 31t-56.5 35.5t-53.5 42.5t-43.5 49t-35.5 58t-21 66.5t-8.5 78q0 138 98 242t255 134v180q0 13 9.5 22.5t22.5 9.5h135q14 0 23 -9t9 -23v-176q57 -6 110.5 -23t87 -33.5 t63.5 -37.5t39 -29t15 -14q17 -18 5 -38l-81 -146q-8 -15 -23 -16q-14 -3 -27 7q-3 3 -14.5 12t-39 26.5t-58.5 32t-74.5 26t-85.5 11.5q-95 0 -155 -43t-60 -111q0 -26 8.5 -48t29.5 -41.5t39.5 -33t56 -31t60.5 -27t70 -27.5q53 -20 81 -31.5t76 -35t75.5 -42.5t62 -50 t53 -63.5t31.5 -76.5t13 -94z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="898" d="M898 1066v-102q0 -14 -9 -23t-23 -9h-168q-23 -144 -129 -234t-276 -110q167 -178 459 -536q14 -16 4 -34q-8 -18 -29 -18h-195q-16 0 -25 12q-306 367 -498 571q-9 9 -9 22v127q0 13 9.5 22.5t22.5 9.5h112q132 0 212.5 43t102.5 125h-427q-14 0 -23 9t-9 23v102 q0 14 9 23t23 9h413q-57 113 -268 113h-145q-13 0 -22.5 9.5t-9.5 22.5v133q0 14 9 23t23 9h832q14 0 23 -9t9 -23v-102q0 -14 -9 -23t-23 -9h-233q47 -61 64 -144h171q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1027" d="M603 0h-172q-13 0 -22.5 9t-9.5 23v330h-288q-13 0 -22.5 9t-9.5 23v103q0 13 9.5 22.5t22.5 9.5h288v85h-288q-13 0 -22.5 9t-9.5 23v104q0 13 9.5 22.5t22.5 9.5h214l-321 578q-8 16 0 32q10 16 28 16h194q19 0 29 -18l215 -425q19 -38 56 -125q10 24 30.5 68t27.5 61 l191 420q8 19 29 19h191q17 0 27 -16q9 -14 1 -31l-313 -579h215q13 0 22.5 -9.5t9.5 -22.5v-104q0 -14 -9.5 -23t-22.5 -9h-290v-85h290q13 0 22.5 -9.5t9.5 -22.5v-103q0 -14 -9.5 -23t-22.5 -9h-290v-330q0 -13 -9.5 -22.5t-22.5 -9.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1664 352v-32q0 -132 -94 -226t-226 -94h-128q-132 0 -226 94t-94 226v480h-224q-2 -102 -14.5 -190.5t-30.5 -156t-48.5 -126.5t-57 -99.5t-67.5 -77.5t-69.5 -58.5t-74 -44t-69 -32t-65.5 -25.5q-4 -2 -32 -13q-8 -2 -12 -2q-22 0 -30 20l-71 178q-5 13 0 25t17 17 q7 3 20 7.5t18 6.5q31 12 46.5 18.5t44.5 20t45.5 26t42 32.5t40.5 42.5t34.5 53.5t30.5 68.5t22.5 83.5t17 103t6.5 123h-256q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h1216q14 0 23 -9t9 -23v-160q0 -14 -9 -23t-23 -9h-224v-512q0 -26 19 -45t45 -19h128q26 0 45 19t19 45 v64q0 14 9 23t23 9h192q14 0 23 -9t9 -23zM1280 1376v-160q0 -14 -9 -23t-23 -9h-960q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h960q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M514 341l81 299h-159l75 -300q1 -1 1 -3t1 -3q0 1 0.5 3.5t0.5 3.5zM630 768l35 128h-292l32 -128h225zM822 768h139l-35 128h-70zM1271 340l78 300h-162l81 -299q0 -1 0.5 -3.5t1.5 -3.5q0 1 0.5 3t0.5 3zM1382 768l33 128h-297l34 -128h230zM1792 736v-64q0 -14 -9 -23 t-23 -9h-213l-164 -616q-7 -24 -31 -24h-159q-24 0 -31 24l-166 616h-209l-167 -616q-7 -24 -31 -24h-159q-11 0 -19.5 7t-10.5 17l-160 616h-208q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h175l-33 128h-142q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h109l-89 344q-5 15 5 28 q10 12 26 12h137q26 0 31 -24l90 -360h359l97 360q7 24 31 24h126q24 0 31 -24l98 -360h365l93 360q5 24 31 24h137q16 0 26 -12q10 -13 5 -28l-91 -344h111q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-145l-34 -128h179q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1167 896q18 -182 -131 -258q117 -28 175 -103t45 -214q-7 -71 -32.5 -125t-64.5 -89t-97 -58.5t-121.5 -34.5t-145.5 -15v-255h-154v251q-80 0 -122 1v-252h-154v255q-18 0 -54 0.5t-55 0.5h-200l31 183h111q50 0 58 51v402h16q-6 1 -16 1v287q-13 68 -89 68h-111v164 l212 -1q64 0 97 1v252h154v-247q82 2 122 2v245h154v-252q79 -7 140 -22.5t113 -45t82.5 -78t36.5 -114.5zM952 351q0 36 -15 64t-37 46t-57.5 30.5t-65.5 18.5t-74 9t-69 3t-64.5 -1t-47.5 -1v-338q8 0 37 -0.5t48 -0.5t53 1.5t58.5 4t57 8.5t55.5 14t47.5 21t39.5 30 t24.5 40t9.5 51zM881 827q0 33 -12.5 58.5t-30.5 42t-48 28t-55 16.5t-61.5 8t-58 2.5t-54 -1t-39.5 -0.5v-307q5 0 34.5 -0.5t46.5 0t50 2t55 5.5t51.5 11t48.5 18.5t37 27t27 38.5t9 51z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1024 160v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1024 416v64q0 14 -9 23t-23 9h-704q-14 0 -23 -9t-9 -23v-64q0 -14 9 -23t23 -9h704q14 0 23 9t9 23zM1280 768v-800q0 -40 -28 -68t-68 -28h-1088q-40 0 -68 28 t-28 68v1344q0 40 28 68t68 28h544v-544q0 -40 28 -68t68 -28h544zM1277 896h-509v509q82 -15 132 -65l312 -312q50 -50 65 -132z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1191 1128h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1572 -23 v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -11v-2l14 2q9 2 30 2h248v119h121zM1661 874v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162 l230 -662h70z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1191 104h177l-72 218l-12 47q-2 16 -2 20h-4l-3 -20q0 -1 -3.5 -18t-7.5 -29zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1661 -150 v-106h-288v106h75l-47 144h-243l-47 -144h75v-106h-287v106h70l230 662h162l230 -662h70zM1572 1001v-233h-584v90l369 529q12 18 21 27l11 9v3q-2 0 -6.5 -0.5t-7.5 -0.5q-12 -3 -30 -3h-232v-115h-120v229h567v-89l-369 -530q-6 -8 -21 -26l-11 -10v-3l14 3q9 1 30 1h248 v119h121z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23zM1792 -32v-192q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832 q14 0 23 -9t9 -23zM1600 480v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1408 992v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1216 1504v-192q0 -14 -9 -23t-23 -9h-256 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1216 -32v-192q0 -14 -9 -23t-23 -9h-256q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h256q14 0 23 -9t9 -23zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192 q14 0 23 -9t9 -23zM1408 480v-192q0 -14 -9 -23t-23 -9h-448q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h448q14 0 23 -9t9 -23zM1600 992v-192q0 -14 -9 -23t-23 -9h-640q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h640q14 0 23 -9t9 -23zM1792 1504v-192q0 -14 -9 -23t-23 -9h-832 q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h832q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" d="M1346 223q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9t9 -23 zM1486 165q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5 t82 -252.5zM1456 882v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165z" />
|
|
||||||
<glyph unicode="" d="M1346 1247q0 63 -44 116t-103 53q-52 0 -83 -37t-31 -94t36.5 -95t104.5 -38q50 0 85 27t35 68zM736 96q0 -12 -10 -24l-319 -319q-10 -9 -23 -9q-12 0 -23 9l-320 320q-15 16 -7 35q8 20 30 20h192v1376q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1376h192q14 0 23 -9 t9 -23zM1456 -142v-114h-469v114h167v432q0 7 0.5 19t0.5 17v16h-2l-7 -12q-8 -13 -26 -31l-62 -58l-82 86l192 185h123v-654h165zM1486 1189q0 -62 -13 -121.5t-41 -114t-68 -95.5t-98.5 -65.5t-127.5 -24.5q-62 0 -108 16q-24 8 -42 15l39 113q15 -7 31 -11q37 -13 75 -13 q84 0 134.5 58.5t66.5 145.5h-2q-21 -23 -61.5 -37t-84.5 -14q-106 0 -173 71.5t-67 172.5q0 105 72 178t181 73q123 0 205 -94.5t82 -252.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M256 192q0 26 -19 45t-45 19q-27 0 -45.5 -19t-18.5 -45q0 -27 18.5 -45.5t45.5 -18.5q26 0 45 18.5t19 45.5zM416 704v-640q0 -26 -19 -45t-45 -19h-288q-26 0 -45 19t-19 45v640q0 26 19 45t45 19h288q26 0 45 -19t19 -45zM1600 704q0 -86 -55 -149q15 -44 15 -76 q3 -76 -43 -137q17 -56 0 -117q-15 -57 -54 -94q9 -112 -49 -181q-64 -76 -197 -78h-36h-76h-17q-66 0 -144 15.5t-121.5 29t-120.5 39.5q-123 43 -158 44q-26 1 -45 19.5t-19 44.5v641q0 25 18 43.5t43 20.5q24 2 76 59t101 121q68 87 101 120q18 18 31 48t17.5 48.5 t13.5 60.5q7 39 12.5 61t19.5 52t34 50q19 19 45 19q46 0 82.5 -10.5t60 -26t40 -40.5t24 -45t12 -50t5 -45t0.5 -39q0 -38 -9.5 -76t-19 -60t-27.5 -56q-3 -6 -10 -18t-11 -22t-8 -24h277q78 0 135 -57t57 -135z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M256 960q0 -26 -19 -45t-45 -19q-27 0 -45.5 19t-18.5 45q0 27 18.5 45.5t45.5 18.5q26 0 45 -18.5t19 -45.5zM416 448v640q0 26 -19 45t-45 19h-288q-26 0 -45 -19t-19 -45v-640q0 -26 19 -45t45 -19h288q26 0 45 19t19 45zM1545 597q55 -61 55 -149q-1 -78 -57.5 -135 t-134.5 -57h-277q4 -14 8 -24t11 -22t10 -18q18 -37 27 -57t19 -58.5t10 -76.5q0 -24 -0.5 -39t-5 -45t-12 -50t-24 -45t-40 -40.5t-60 -26t-82.5 -10.5q-26 0 -45 19q-20 20 -34 50t-19.5 52t-12.5 61q-9 42 -13.5 60.5t-17.5 48.5t-31 48q-33 33 -101 120q-49 64 -101 121 t-76 59q-25 2 -43 20.5t-18 43.5v641q0 26 19 44.5t45 19.5q35 1 158 44q77 26 120.5 39.5t121.5 29t144 15.5h17h76h36q133 -2 197 -78q58 -69 49 -181q39 -37 54 -94q17 -61 0 -117q46 -61 43 -137q0 -32 -15 -76z" />
|
|
||||||
<glyph unicode="" d="M919 233v157q0 50 -29 50q-17 0 -33 -16v-224q16 -16 33 -16q29 0 29 49zM1103 355h66v34q0 51 -33 51t-33 -51v-34zM532 621v-70h-80v-423h-74v423h-78v70h232zM733 495v-367h-67v40q-39 -45 -76 -45q-33 0 -42 28q-6 16 -6 54v290h66v-270q0 -24 1 -26q1 -15 15 -15 q20 0 42 31v280h67zM985 384v-146q0 -52 -7 -73q-12 -42 -53 -42q-35 0 -68 41v-36h-67v493h67v-161q32 40 68 40q41 0 53 -42q7 -21 7 -74zM1236 255v-9q0 -29 -2 -43q-3 -22 -15 -40q-27 -40 -80 -40q-52 0 -81 38q-21 27 -21 86v129q0 59 20 86q29 38 80 38t78 -38 q21 -28 21 -86v-76h-133v-65q0 -51 34 -51q24 0 30 26q0 1 0.5 7t0.5 16.5v21.5h68zM785 1079v-156q0 -51 -32 -51t-32 51v156q0 52 32 52t32 -52zM1318 366q0 177 -19 260q-10 44 -43 73.5t-76 34.5q-136 15 -412 15q-275 0 -411 -15q-44 -5 -76.5 -34.5t-42.5 -73.5 q-20 -87 -20 -260q0 -176 20 -260q10 -43 42.5 -73t75.5 -35q137 -15 412 -15t412 15q43 5 75.5 35t42.5 73q20 84 20 260zM563 1017l90 296h-75l-51 -195l-53 195h-78l24 -69t23 -69q35 -103 46 -158v-201h74v201zM852 936v130q0 58 -21 87q-29 38 -78 38q-51 0 -78 -38 q-21 -29 -21 -87v-130q0 -58 21 -87q27 -38 78 -38q49 0 78 38q21 27 21 87zM1033 816h67v370h-67v-283q-22 -31 -42 -31q-15 0 -16 16q-1 2 -1 26v272h-67v-293q0 -37 6 -55q11 -27 43 -27q36 0 77 45v-40zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960 q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" d="M971 292v-211q0 -67 -39 -67q-23 0 -45 22v301q22 22 45 22q39 0 39 -67zM1309 291v-46h-90v46q0 68 45 68t45 -68zM343 509h107v94h-312v-94h105v-569h100v569zM631 -60h89v494h-89v-378q-30 -42 -57 -42q-18 0 -21 21q-1 3 -1 35v364h-89v-391q0 -49 8 -73 q12 -37 58 -37q48 0 102 61v-54zM1060 88v197q0 73 -9 99q-17 56 -71 56q-50 0 -93 -54v217h-89v-663h89v48q45 -55 93 -55q54 0 71 55q9 27 9 100zM1398 98v13h-91q0 -51 -2 -61q-7 -36 -40 -36q-46 0 -46 69v87h179v103q0 79 -27 116q-39 51 -106 51q-68 0 -107 -51 q-28 -37 -28 -116v-173q0 -79 29 -116q39 -51 108 -51q72 0 108 53q18 27 21 54q2 9 2 58zM790 1011v210q0 69 -43 69t-43 -69v-210q0 -70 43 -70t43 70zM1509 260q0 -234 -26 -350q-14 -59 -58 -99t-102 -46q-184 -21 -555 -21t-555 21q-58 6 -102.5 46t-57.5 99 q-26 112 -26 350q0 234 26 350q14 59 58 99t103 47q183 20 554 20t555 -20q58 -7 102.5 -47t57.5 -99q26 -112 26 -350zM511 1536h102l-121 -399v-271h-100v271q-14 74 -61 212q-37 103 -65 187h106l71 -263zM881 1203v-175q0 -81 -28 -118q-37 -51 -106 -51q-67 0 -105 51 q-28 38 -28 118v175q0 80 28 117q38 51 105 51q69 0 106 -51q28 -37 28 -117zM1216 1365v-499h-91v55q-53 -62 -103 -62q-46 0 -59 37q-8 24 -8 75v394h91v-367q0 -33 1 -35q3 -22 21 -22q27 0 57 43v381h91z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M597 869q-10 -18 -257 -456q-27 -46 -65 -46h-239q-21 0 -31 17t0 36l253 448q1 0 0 1l-161 279q-12 22 -1 37q9 15 32 15h239q40 0 66 -45zM1403 1511q11 -16 0 -37l-528 -934v-1l336 -615q11 -20 1 -37q-10 -15 -32 -15h-239q-42 0 -66 45l-339 622q18 32 531 942 q25 45 64 45h241q22 0 31 -15z" />
|
|
||||||
<glyph unicode="" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
|
|
||||||
<glyph unicode="" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
|
|
||||||
<glyph unicode="" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
|
|
||||||
<glyph unicode="" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M815 677q8 -63 -50.5 -101t-111.5 -6q-39 17 -53.5 58t-0.5 82t52 58q36 18 72.5 12t64 -35.5t27.5 -67.5zM926 698q-14 107 -113 164t-197 13q-63 -28 -100.5 -88.5t-34.5 -129.5q4 -91 77.5 -155t165.5 -56q91 8 152 84t50 168zM1165 1240q-20 27 -56 44.5t-58 22 t-71 12.5q-291 47 -566 -2q-43 -7 -66 -12t-55 -22t-50 -43q30 -28 76 -45.5t73.5 -22t87.5 -11.5q228 -29 448 -1q63 8 89.5 12t72.5 21.5t75 46.5zM1222 205q-8 -26 -15.5 -76.5t-14 -84t-28.5 -70t-58 -56.5q-86 -48 -189.5 -71.5t-202 -22t-201.5 18.5q-46 8 -81.5 18 t-76.5 27t-73 43.5t-52 61.5q-25 96 -57 292l6 16l18 9q223 -148 506.5 -148t507.5 148q21 -6 24 -23t-5 -45t-8 -37zM1403 1166q-26 -167 -111 -655q-5 -30 -27 -56t-43.5 -40t-54.5 -31q-252 -126 -610 -88q-248 27 -394 139q-15 12 -25.5 26.5t-17 35t-9 34t-6 39.5 t-5.5 35q-9 50 -26.5 150t-28 161.5t-23.5 147.5t-22 158q3 26 17.5 48.5t31.5 37.5t45 30t46 22.5t48 18.5q125 46 313 64q379 37 676 -50q155 -46 215 -122q16 -20 16.5 -51t-5.5 -54z" />
|
|
||||||
<glyph unicode="" d="M848 666q0 43 -41 66t-77 1q-43 -20 -42.5 -72.5t43.5 -70.5q39 -23 81 4t36 72zM928 682q8 -66 -36 -121t-110 -61t-119 40t-56 113q-2 49 25.5 93t72.5 64q70 31 141.5 -10t81.5 -118zM1100 1073q-20 -21 -53.5 -34t-53 -16t-63.5 -8q-155 -20 -324 0q-44 6 -63 9.5 t-52.5 16t-54.5 32.5q13 19 36 31t40 15.5t47 8.5q198 35 408 1q33 -5 51 -8.5t43 -16t39 -31.5zM1142 327q0 7 5.5 26.5t3 32t-17.5 16.5q-161 -106 -365 -106t-366 106l-12 -6l-5 -12q26 -154 41 -210q47 -81 204 -108q249 -46 428 53q34 19 49 51.5t22.5 85.5t12.5 71z M1272 1020q9 53 -8 75q-43 55 -155 88q-216 63 -487 36q-132 -12 -226 -46q-38 -15 -59.5 -25t-47 -34t-29.5 -54q8 -68 19 -138t29 -171t24 -137q1 -5 5 -31t7 -36t12 -27t22 -28q105 -80 284 -100q259 -28 440 63q24 13 39.5 23t31 29t19.5 40q48 267 80 473zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M390 1408h219v-388h364v-241h-364v-394q0 -136 14 -172q13 -37 52 -60q50 -31 117 -31q117 0 232 76v-242q-102 -48 -178 -65q-77 -19 -173 -19q-105 0 -186 27q-78 25 -138 75q-58 51 -79 105q-22 54 -22 161v539h-170v217q91 30 155 84q64 55 103 132q39 78 54 196z " />
|
|
||||||
<glyph unicode="" d="M1123 127v181q-88 -56 -174 -56q-51 0 -88 23q-29 17 -39 45q-11 30 -11 129v295h274v181h-274v291h-164q-11 -90 -40 -147t-78 -99q-48 -40 -116 -63v-163h127v-404q0 -78 17 -121q17 -42 59 -78q43 -37 104 -57q62 -20 140 -20q67 0 129 14q57 13 134 49zM1536 1120 v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M765 237q8 -19 -5 -35l-350 -384q-10 -10 -23 -10q-14 0 -24 10l-355 384q-13 16 -5 35q9 19 29 19h224v1248q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-1248h224q21 0 29 -19z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="768" d="M765 1043q-9 -19 -29 -19h-224v-1248q0 -14 -9 -23t-23 -9h-192q-14 0 -23 9t-9 23v1248h-224q-21 0 -29 19t5 35l350 384q10 10 23 10q14 0 24 -10l355 -384q13 -16 5 -35z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1792 736v-192q0 -14 -9 -23t-23 -9h-1248v-224q0 -21 -19 -29t-35 5l-384 350q-10 10 -10 23q0 14 10 24l384 354q16 14 35 6q19 -9 19 -29v-224h1248q14 0 23 -9t9 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1728 643q0 -14 -10 -24l-384 -354q-16 -14 -35 -6q-19 9 -19 29v224h-1248q-14 0 -23 9t-9 23v192q0 14 9 23t23 9h1248v224q0 21 19 29t35 -5l384 -350q10 -10 10 -23z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M1393 321q-39 -125 -123 -250q-129 -196 -257 -196q-49 0 -140 32q-86 32 -151 32q-61 0 -142 -33q-81 -34 -132 -34q-152 0 -301 259q-147 261 -147 503q0 228 113 374q112 144 284 144q72 0 177 -30q104 -30 138 -30q45 0 143 34q102 34 173 34q119 0 213 -65 q52 -36 104 -100q-79 -67 -114 -118q-65 -94 -65 -207q0 -124 69 -223t158 -126zM1017 1494q0 -61 -29 -136q-30 -75 -93 -138q-54 -54 -108 -72q-37 -11 -104 -17q3 149 78 257q74 107 250 148q1 -3 2.5 -11t2.5 -11q0 -4 0.5 -10t0.5 -10z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M682 530v-651l-682 94v557h682zM682 1273v-659h-682v565zM1664 530v-786l-907 125v661h907zM1664 1408v-794h-907v669z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1408" d="M493 1053q16 0 27.5 11.5t11.5 27.5t-11.5 27.5t-27.5 11.5t-27 -11.5t-11 -27.5t11 -27.5t27 -11.5zM915 1053q16 0 27 11.5t11 27.5t-11 27.5t-27 11.5t-27.5 -11.5t-11.5 -27.5t11.5 -27.5t27.5 -11.5zM103 869q42 0 72 -30t30 -72v-430q0 -43 -29.5 -73t-72.5 -30 t-73 30t-30 73v430q0 42 30 72t73 30zM1163 850v-666q0 -46 -32 -78t-77 -32h-75v-227q0 -43 -30 -73t-73 -30t-73 30t-30 73v227h-138v-227q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73l-1 227h-74q-46 0 -78 32t-32 78v666h918zM931 1255q107 -55 171 -153.5t64 -215.5 h-925q0 117 64 215.5t172 153.5l-71 131q-7 13 5 20q13 6 20 -6l72 -132q95 42 201 42t201 -42l72 132q7 12 20 6q12 -7 5 -20zM1408 767v-430q0 -43 -30 -73t-73 -30q-42 0 -72 30t-30 73v430q0 43 30 72.5t72 29.5q43 0 73 -29.5t30 -72.5z" />
|
|
||||||
<glyph unicode="" d="M663 1125q-11 -1 -15.5 -10.5t-8.5 -9.5q-5 -1 -5 5q0 12 19 15h10zM750 1111q-4 -1 -11.5 6.5t-17.5 4.5q24 11 32 -2q3 -6 -3 -9zM399 684q-4 1 -6 -3t-4.5 -12.5t-5.5 -13.5t-10 -13q-7 -10 -1 -12q4 -1 12.5 7t12.5 18q1 3 2 7t2 6t1.5 4.5t0.5 4v3t-1 2.5t-3 2z M1254 325q0 18 -55 42q4 15 7.5 27.5t5 26t3 21.5t0.5 22.5t-1 19.5t-3.5 22t-4 20.5t-5 25t-5.5 26.5q-10 48 -47 103t-72 75q24 -20 57 -83q87 -162 54 -278q-11 -40 -50 -42q-31 -4 -38.5 18.5t-8 83.5t-11.5 107q-9 39 -19.5 69t-19.5 45.5t-15.5 24.5t-13 15t-7.5 7 q-14 62 -31 103t-29.5 56t-23.5 33t-15 40q-4 21 6 53.5t4.5 49.5t-44.5 25q-15 3 -44.5 18t-35.5 16q-8 1 -11 26t8 51t36 27q37 3 51 -30t4 -58q-11 -19 -2 -26.5t30 -0.5q13 4 13 36v37q-5 30 -13.5 50t-21 30.5t-23.5 15t-27 7.5q-107 -8 -89 -134q0 -15 -1 -15 q-9 9 -29.5 10.5t-33 -0.5t-15.5 5q1 57 -16 90t-45 34q-27 1 -41.5 -27.5t-16.5 -59.5q-1 -15 3.5 -37t13 -37.5t15.5 -13.5q10 3 16 14q4 9 -7 8q-7 0 -15.5 14.5t-9.5 33.5q-1 22 9 37t34 14q17 0 27 -21t9.5 -39t-1.5 -22q-22 -15 -31 -29q-8 -12 -27.5 -23.5 t-20.5 -12.5q-13 -14 -15.5 -27t7.5 -18q14 -8 25 -19.5t16 -19t18.5 -13t35.5 -6.5q47 -2 102 15q2 1 23 7t34.5 10.5t29.5 13t21 17.5q9 14 20 8q5 -3 6.5 -8.5t-3 -12t-16.5 -9.5q-20 -6 -56.5 -21.5t-45.5 -19.5q-44 -19 -70 -23q-25 -5 -79 2q-10 2 -9 -2t17 -19 q25 -23 67 -22q17 1 36 7t36 14t33.5 17.5t30 17t24.5 12t17.5 2.5t8.5 -11q0 -2 -1 -4.5t-4 -5t-6 -4.5t-8.5 -5t-9 -4.5t-10 -5t-9.5 -4.5q-28 -14 -67.5 -44t-66.5 -43t-49 -1q-21 11 -63 73q-22 31 -25 22q-1 -3 -1 -10q0 -25 -15 -56.5t-29.5 -55.5t-21 -58t11.5 -63 q-23 -6 -62.5 -90t-47.5 -141q-2 -18 -1.5 -69t-5.5 -59q-8 -24 -29 -3q-32 31 -36 94q-2 28 4 56q4 19 -1 18l-4 -5q-36 -65 10 -166q5 -12 25 -28t24 -20q20 -23 104 -90.5t93 -76.5q16 -15 17.5 -38t-14 -43t-45.5 -23q8 -15 29 -44.5t28 -54t7 -70.5q46 24 7 92 q-4 8 -10.5 16t-9.5 12t-2 6q3 5 13 9.5t20 -2.5q46 -52 166 -36q133 15 177 87q23 38 34 30q12 -6 10 -52q-1 -25 -23 -92q-9 -23 -6 -37.5t24 -15.5q3 19 14.5 77t13.5 90q2 21 -6.5 73.5t-7.5 97t23 70.5q15 18 51 18q1 37 34.5 53t72.5 10.5t60 -22.5zM626 1152 q3 17 -2.5 30t-11.5 15q-9 2 -9 -7q2 -5 5 -6q10 0 7 -15q-3 -20 8 -20q3 0 3 3zM1045 955q-2 8 -6.5 11.5t-13 5t-14.5 5.5q-5 3 -9.5 8t-7 8t-5.5 6.5t-4 4t-4 -1.5q-14 -16 7 -43.5t39 -31.5q9 -1 14.5 8t3.5 20zM867 1168q0 11 -5 19.5t-11 12.5t-9 3q-14 -1 -7 -7l4 -2 q14 -4 18 -31q0 -3 8 2zM921 1401q0 2 -2.5 5t-9 7t-9.5 6q-15 15 -24 15q-9 -1 -11.5 -7.5t-1 -13t-0.5 -12.5q-1 -4 -6 -10.5t-6 -9t3 -8.5q4 -3 8 0t11 9t15 9q1 1 9 1t15 2t9 7zM1486 60q20 -12 31 -24.5t12 -24t-2.5 -22.5t-15.5 -22t-23.5 -19.5t-30 -18.5 t-31.5 -16.5t-32 -15.5t-27 -13q-38 -19 -85.5 -56t-75.5 -64q-17 -16 -68 -19.5t-89 14.5q-18 9 -29.5 23.5t-16.5 25.5t-22 19.5t-47 9.5q-44 1 -130 1q-19 0 -57 -1.5t-58 -2.5q-44 -1 -79.5 -15t-53.5 -30t-43.5 -28.5t-53.5 -11.5q-29 1 -111 31t-146 43q-19 4 -51 9.5 t-50 9t-39.5 9.5t-33.5 14.5t-17 19.5q-10 23 7 66.5t18 54.5q1 16 -4 40t-10 42.5t-4.5 36.5t10.5 27q14 12 57 14t60 12q30 18 42 35t12 51q21 -73 -32 -106q-32 -20 -83 -15q-34 3 -43 -10q-13 -15 5 -57q2 -6 8 -18t8.5 -18t4.5 -17t1 -22q0 -15 -17 -49t-14 -48 q3 -17 37 -26q20 -6 84.5 -18.5t99.5 -20.5q24 -6 74 -22t82.5 -23t55.5 -4q43 6 64.5 28t23 48t-7.5 58.5t-19 52t-20 36.5q-121 190 -169 242q-68 74 -113 40q-11 -9 -15 15q-3 16 -2 38q1 29 10 52t24 47t22 42q8 21 26.5 72t29.5 78t30 61t39 54q110 143 124 195 q-12 112 -16 310q-2 90 24 151.5t106 104.5q39 21 104 21q53 1 106 -13.5t89 -41.5q57 -42 91.5 -121.5t29.5 -147.5q-5 -95 30 -214q34 -113 133 -218q55 -59 99.5 -163t59.5 -191q8 -49 5 -84.5t-12 -55.5t-20 -22q-10 -2 -23.5 -19t-27 -35.5t-40.5 -33.5t-61 -14 q-18 1 -31.5 5t-22.5 13.5t-13.5 15.5t-11.5 20.5t-9 19.5q-22 37 -41 30t-28 -49t7 -97q20 -70 1 -195q-10 -65 18 -100.5t73 -33t85 35.5q59 49 89.5 66.5t103.5 42.5q53 18 77 36.5t18.5 34.5t-25 28.5t-51.5 23.5q-33 11 -49.5 48t-15 72.5t15.5 47.5q1 -31 8 -56.5 t14.5 -40.5t20.5 -28.5t21 -19t21.5 -13t16.5 -9.5z" />
|
|
||||||
<glyph unicode="" d="M1024 36q-42 241 -140 498h-2l-2 -1q-16 -6 -43 -16.5t-101 -49t-137 -82t-131 -114.5t-103 -148l-15 11q184 -150 418 -150q132 0 256 52zM839 643q-21 49 -53 111q-311 -93 -673 -93q-1 -7 -1 -21q0 -124 44 -236.5t124 -201.5q50 89 123.5 166.5t142.5 124.5t130.5 81 t99.5 48l37 13q4 1 13 3.5t13 4.5zM732 855q-120 213 -244 378q-138 -65 -234 -186t-128 -272q302 0 606 80zM1416 536q-210 60 -409 29q87 -239 128 -469q111 75 185 189.5t96 250.5zM611 1277q-1 0 -2 -1q1 1 2 1zM1201 1132q-185 164 -433 164q-76 0 -155 -19 q131 -170 246 -382q69 26 130 60.5t96.5 61.5t65.5 57t37.5 40.5zM1424 647q-3 232 -149 410l-1 -1q-9 -12 -19 -24.5t-43.5 -44.5t-71 -60.5t-100 -65t-131.5 -64.5q25 -53 44 -95q2 -6 6.5 -17.5t7.5 -16.5q36 5 74.5 7t73.5 2t69 -1.5t64 -4t56.5 -5.5t48 -6.5t36.5 -6 t25 -4.5zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" d="M1173 473q0 50 -19.5 91.5t-48.5 68.5t-73 49t-82.5 34t-87.5 23l-104 24q-30 7 -44 10.5t-35 11.5t-30 16t-16.5 21t-7.5 30q0 77 144 77q43 0 77 -12t54 -28.5t38 -33.5t40 -29t48 -12q47 0 75.5 32t28.5 77q0 55 -56 99.5t-142 67.5t-182 23q-68 0 -132 -15.5 t-119.5 -47t-89 -87t-33.5 -128.5q0 -61 19 -106.5t56 -75.5t80 -48.5t103 -32.5l146 -36q90 -22 112 -36q32 -20 32 -60q0 -39 -40 -64.5t-105 -25.5q-51 0 -91.5 16t-65 38.5t-45.5 45t-46 38.5t-54 16q-50 0 -75.5 -30t-25.5 -75q0 -92 122 -157.5t291 -65.5 q73 0 140 18.5t122.5 53.5t88.5 93.5t33 131.5zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5q-130 0 -234 80q-77 -16 -150 -16q-143 0 -273.5 55.5t-225 150t-150 225t-55.5 273.5q0 73 16 150q-80 104 -80 234q0 159 112.5 271.5t271.5 112.5q130 0 234 -80 q77 16 150 16q143 0 273.5 -55.5t225 -150t150 -225t55.5 -273.5q0 -73 -16 -150q80 -104 80 -234z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1483 512l-587 -587q-52 -53 -127.5 -53t-128.5 53l-587 587q-53 53 -53 128t53 128l587 587q53 53 128 53t128 -53l265 -265l-398 -399l-188 188q-42 42 -99 42q-59 0 -100 -41l-120 -121q-42 -40 -42 -99q0 -58 42 -100l406 -408q30 -28 67 -37l6 -4h28q60 0 99 41 l619 619l2 -3q53 -53 53 -128t-53 -128zM1406 1138l120 -120q14 -15 14 -36t-14 -36l-730 -730q-17 -15 -37 -15v0q-4 0 -6 1q-18 2 -30 14l-407 408q-14 15 -14 36t14 35l121 120q13 15 35 15t36 -15l252 -252l574 575q15 15 36 15t36 -15z" />
|
|
||||||
<glyph unicode="" d="M704 192v1024q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-1024q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1376 576v640q0 14 -9 23t-23 9h-480q-14 0 -23 -9t-9 -23v-640q0 -14 9 -23t23 -9h480q14 0 23 9t9 23zM1536 1344v-1408q0 -26 -19 -45t-45 -19h-1408 q-26 0 -45 19t-19 45v1408q0 26 19 45t45 19h1408q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1280" d="M1280 480q0 -40 -28 -68t-68 -28q-51 0 -80 43l-227 341h-45v-132l247 -411q9 -15 9 -33q0 -26 -19 -45t-45 -19h-192v-272q0 -46 -33 -79t-79 -33h-160q-46 0 -79 33t-33 79v272h-192q-26 0 -45 19t-19 45q0 18 9 33l247 411v132h-45l-227 -341q-29 -43 -80 -43 q-40 0 -68 28t-28 68q0 29 16 53l256 384q73 107 176 107h384q103 0 176 -107l256 -384q16 -24 16 -53zM864 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1024" d="M1024 832v-416q0 -40 -28 -68t-68 -28t-68 28t-28 68v352h-64v-912q0 -46 -33 -79t-79 -33t-79 33t-33 79v464h-64v-464q0 -46 -33 -79t-79 -33t-79 33t-33 79v912h-64v-352q0 -40 -28 -68t-68 -28t-68 28t-28 68v416q0 80 56 136t136 56h640q80 0 136 -56t56 -136z M736 1280q0 -93 -65.5 -158.5t-158.5 -65.5t-158.5 65.5t-65.5 158.5t65.5 158.5t158.5 65.5t158.5 -65.5t65.5 -158.5z" />
|
|
||||||
<glyph unicode="" d="M773 234l350 473q16 22 24.5 59t-6 85t-61.5 79q-40 26 -83 25.5t-73.5 -17.5t-54.5 -45q-36 -40 -96 -40q-59 0 -95 40q-24 28 -54.5 45t-73.5 17.5t-84 -25.5q-46 -31 -60.5 -79t-6 -85t24.5 -59zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103 t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1472 640q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5t-223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5t45.5 -223.5t123 -184t184 -123t223.5 -45.5t223.5 45.5t184 123t123 184t45.5 223.5zM1748 363q-4 -15 -20 -20l-292 -96v-306q0 -16 -13 -26q-15 -10 -29 -4 l-292 94l-180 -248q-10 -13 -26 -13t-26 13l-180 248l-292 -94q-14 -6 -29 4q-13 10 -13 26v306l-292 96q-16 5 -20 20q-5 17 4 29l180 248l-180 248q-9 13 -4 29q4 15 20 20l292 96v306q0 16 13 26q15 10 29 4l292 -94l180 248q9 12 26 12t26 -12l180 -248l292 94 q14 6 29 -4q13 -10 13 -26v-306l292 -96q16 -5 20 -20q5 -16 -4 -29l-180 -248l180 -248q9 -12 4 -29z" />
|
|
||||||
<glyph unicode="" d="M1262 233q-54 -9 -110 -9q-182 0 -337 90t-245 245t-90 337q0 192 104 357q-201 -60 -328.5 -229t-127.5 -384q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51q144 0 273.5 61.5t220.5 171.5zM1465 318q-94 -203 -283.5 -324.5t-413.5 -121.5q-156 0 -298 61 t-245 164t-164 245t-61 298q0 153 57.5 292.5t156 241.5t235.5 164.5t290 68.5q44 2 61 -39q18 -41 -15 -72q-86 -78 -131.5 -181.5t-45.5 -218.5q0 -148 73 -273t198 -198t273 -73q118 0 228 51q41 18 72 -13q14 -14 17.5 -34t-4.5 -38z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M1088 704q0 26 -19 45t-45 19h-256q-26 0 -45 -19t-19 -45t19 -45t45 -19h256q26 0 45 19t19 45zM1664 896v-960q0 -26 -19 -45t-45 -19h-1408q-26 0 -45 19t-19 45v960q0 26 19 45t45 19h1408q26 0 45 -19t19 -45zM1728 1344v-256q0 -26 -19 -45t-45 -19h-1536 q-26 0 -45 19t-19 45v256q0 26 19 45t45 19h1536q26 0 45 -19t19 -45z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1664" d="M1632 576q0 -26 -19 -45t-45 -19h-224q0 -171 -67 -290l208 -209q19 -19 19 -45t-19 -45q-18 -19 -45 -19t-45 19l-198 197q-5 -5 -15 -13t-42 -28.5t-65 -36.5t-82 -29t-97 -13v896h-128v-896q-51 0 -101.5 13.5t-87 33t-66 39t-43.5 32.5l-15 14l-183 -207 q-20 -21 -48 -21q-24 0 -43 16q-19 18 -20.5 44.5t15.5 46.5l202 227q-58 114 -58 274h-224q-26 0 -45 19t-19 45t19 45t45 19h224v294l-173 173q-19 19 -19 45t19 45t45 19t45 -19l173 -173h844l173 173q19 19 45 19t45 -19t19 -45t-19 -45l-173 -173v-294h224q26 0 45 -19 t19 -45zM1152 1152h-640q0 133 93.5 226.5t226.5 93.5t226.5 -93.5t93.5 -226.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M1917 1016q23 -64 -150 -294q-24 -32 -65 -85q-78 -100 -90 -131q-17 -41 14 -81q17 -21 81 -82h1l1 -1l1 -1l2 -2q141 -131 191 -221q3 -5 6.5 -12.5t7 -26.5t-0.5 -34t-25 -27.5t-59 -12.5l-256 -4q-24 -5 -56 5t-52 22l-20 12q-30 21 -70 64t-68.5 77.5t-61 58 t-56.5 15.5q-3 -1 -8 -3.5t-17 -14.5t-21.5 -29.5t-17 -52t-6.5 -77.5q0 -15 -3.5 -27.5t-7.5 -18.5l-4 -5q-18 -19 -53 -22h-115q-71 -4 -146 16.5t-131.5 53t-103 66t-70.5 57.5l-25 24q-10 10 -27.5 30t-71.5 91t-106 151t-122.5 211t-130.5 272q-6 16 -6 27t3 16l4 6 q15 19 57 19l274 2q12 -2 23 -6.5t16 -8.5l5 -3q16 -11 24 -32q20 -50 46 -103.5t41 -81.5l16 -29q29 -60 56 -104t48.5 -68.5t41.5 -38.5t34 -14t27 5q2 1 5 5t12 22t13.5 47t9.5 81t0 125q-2 40 -9 73t-14 46l-6 12q-25 34 -85 43q-13 2 5 24q17 19 38 30q53 26 239 24 q82 -1 135 -13q20 -5 33.5 -13.5t20.5 -24t10.5 -32t3.5 -45.5t-1 -55t-2.5 -70.5t-1.5 -82.5q0 -11 -1 -42t-0.5 -48t3.5 -40.5t11.5 -39t22.5 -24.5q8 -2 17 -4t26 11t38 34.5t52 67t68 107.5q60 104 107 225q4 10 10 17.5t11 10.5l4 3l5 2.5t13 3t20 0.5l288 2 q39 5 64 -2.5t31 -16.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" d="M675 252q21 34 11 69t-45 50q-34 14 -73 1t-60 -46q-22 -34 -13 -68.5t43 -50.5t74.5 -2.5t62.5 47.5zM769 373q8 13 3.5 26.5t-17.5 18.5q-14 5 -28.5 -0.5t-21.5 -18.5q-17 -31 13 -45q14 -5 29 0.5t22 18.5zM943 266q-45 -102 -158 -150t-224 -12 q-107 34 -147.5 126.5t6.5 187.5q47 93 151.5 139t210.5 19q111 -29 158.5 -119.5t2.5 -190.5zM1255 426q-9 96 -89 170t-208.5 109t-274.5 21q-223 -23 -369.5 -141.5t-132.5 -264.5q9 -96 89 -170t208.5 -109t274.5 -21q223 23 369.5 141.5t132.5 264.5zM1563 422 q0 -68 -37 -139.5t-109 -137t-168.5 -117.5t-226 -83t-270.5 -31t-275 33.5t-240.5 93t-171.5 151t-65 199.5q0 115 69.5 245t197.5 258q169 169 341.5 236t246.5 -7q65 -64 20 -209q-4 -14 -1 -20t10 -7t14.5 0.5t13.5 3.5l6 2q139 59 246 59t153 -61q45 -63 0 -178 q-2 -13 -4.5 -20t4.5 -12.5t12 -7.5t17 -6q57 -18 103 -47t80 -81.5t34 -116.5zM1489 1046q42 -47 54.5 -108.5t-6.5 -117.5q-8 -23 -29.5 -34t-44.5 -4q-23 8 -34 29.5t-4 44.5q20 63 -24 111t-107 35q-24 -5 -45 8t-25 37q-5 24 8 44.5t37 25.5q60 13 119 -5.5t101 -65.5z M1670 1209q87 -96 112.5 -222.5t-13.5 -241.5q-9 -27 -34 -40t-52 -4t-40 34t-5 52q28 82 10 172t-80 158q-62 69 -148 95.5t-173 8.5q-28 -6 -52 9.5t-30 43.5t9.5 51.5t43.5 29.5q123 26 244 -11.5t208 -134.5z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1920" d="M805 163q-122 -67 -261 -67q-141 0 -261 67q98 61 167 149t94 191q25 -103 94 -191t167 -149zM453 1176v-344q0 -179 -89.5 -326t-234.5 -217q-129 152 -129 351q0 200 129.5 352t323.5 184zM958 991q-128 -152 -128 -351q0 -201 128 -351q-145 70 -234.5 218t-89.5 328 v341q196 -33 324 -185zM1638 163q-122 -67 -261 -67q-141 0 -261 67q98 61 167 149t94 191q25 -103 94 -191t167 -149zM1286 1176v-344q0 -179 -91 -326t-237 -217v0q133 154 133 351q0 195 -133 351q129 151 328 185zM1920 640q0 -201 -129 -351q-145 70 -234.5 218 t-89.5 328v341q194 -32 323.5 -184t129.5 -352z" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" />
|
|
||||||
<glyph unicode="" horiz-adv-x="1792" />
|
|
||||||
</font>
|
|
||||||
</defs></svg>
|
|
||||||
|
Before Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 225 B |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 12 KiB |