Compare commits

...

330 Commits

Author SHA1 Message Date
Бородин Роман c3c3031f37 listen 0.0.0.0 2023-10-01 14:20:36 +03:00
Бородин Роман a7b9baaaaf . 2023-07-31 13:27:08 +03:00
Бородин Роман 593d9241be -i устарела 2023-07-31 13:24:46 +03:00
Бородин Роман 35ac5a50d6 oauth 2023-07-31 12:46:18 +03:00
Liang Ding f6a72ff464
🎨 加入只读模式配置 2022-08-09 10:11:53 +08:00
Liang Ding 24d4654030
🎨 加入只读模式配置 2022-08-09 10:09:38 +08:00
Liang Ding 58611c8f1e
🎨 加入只读模式配置 2022-08-09 10:05:05 +08:00
Liang Ding 9076912b9d
🎨 加入只读模式配置 2022-08-09 10:01:55 +08:00
Liang Ding 68fee17793
🎨 加入只读模式配置 2022-08-09 09:56:45 +08:00
Liang Ding 877e96df4d
🎨 加入只读模式配置 2022-08-09 09:43:46 +08:00
D 029982f00e
Merge pull request #14 from 88250/dependabot/npm_and_yarn/yargs-parser-5.0.1
⬆️ Bump yargs-parser from 5.0.0 to 5.0.1
2021-04-11 23:52:23 +08:00
dependabot[bot] 5cfb9ca5fb
⬆️ Bump yargs-parser from 5.0.0 to 5.0.1
Bumps [yargs-parser](https://github.com/yargs/yargs-parser) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/yargs/yargs-parser/releases)
- [Changelog](https://github.com/yargs/yargs-parser/blob/v5.0.1/CHANGELOG.md)
- [Commits](https://github.com/yargs/yargs-parser/compare/v5.0.0...v5.0.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-11 14:43:47 +00:00
Liang Ding 93654cfa32
🎨 社区域名迁移 2021-04-11 22:42:50 +08:00
Liang Ding de0110eadf
🎨 Docker 容器限制 CPU 和内存 2021-03-05 20:11:01 +08:00
Liang Ding 2e53a02446
🎨 Docker 容器限制 CPU 和内存 2021-03-05 20:09:37 +08:00
Liang Ding 3a4aabc297
🎨 链滴 2021-03-05 20:02:08 +08:00
Liang Ding 9b8c3eb888
🎨 Docker 容器限制 CPU 和内存 2021-03-05 20:00:09 +08:00
Liang Ding 7f9f726f71
🎨 社区域名迁移 2020-10-13 21:56:20 +08:00
Liang Ding a0ec5a70b7
Merge remote-tracking branch 'origin/master' 2020-06-10 12:19:20 +08:00
Liang Ding 51a3f6b932
🔒 修复登录验证安全漏洞 Fix #9 2020-06-10 12:18:53 +08:00
D 791f438ff9
Merge pull request #4 from 88250/dependabot/npm_and_yarn/acorn-5.7.4
⬆️ Bump acorn from 5.7.3 to 5.7.4
2020-05-17 10:37:12 +08:00
Liang Ding e3c3b165c8
🎨 #7 2020-04-19 13:06:57 +08:00
Liang Ding 3256f60990
🎨 go1.13+ 自动完成失效问题 Fix #6 2020-04-09 13:15:50 +08:00
Liang Ding e51b78ea85
⬆️ 升级依赖 2020-04-09 13:14:08 +08:00
Liang Ding 3bf430e35f
🎨 go1.13+ 自动完成失效问题 #6 2020-04-09 11:58:50 +08:00
dependabot[bot] 12c5da94f6
⬆️ Bump acorn from 5.7.3 to 5.7.4
Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)

Signed-off-by: dependabot[bot] <support@github.com>
2020-04-06 15:26:50 +00:00
Liang Ding cd9d0165c0
⬆️ 更新依赖 2020-01-24 18:28:07 +08:00
Liang Ding 231a35c725
🎨 关闭账号迁移公告 2020-01-22 10:30:15 +08:00
Liang Ding dbd41730ae
🎨 Fix #3 2020-01-18 17:17:15 +08:00
Liang Ding c84ead6140
🎨 #3 2020-01-18 17:16:12 +08:00
Liang Ding 786a0f5c4a
🎨 #3 2020-01-18 17:14:08 +08:00
Liang Ding 624ef3a152
🎨 #3 2020-01-18 17:04:37 +08:00
Liang Ding 1287018811
🎨 #3 2020-01-18 17:02:56 +08:00
Liang Ding 3193610b7f
🎨 #3 2020-01-18 16:57:57 +08:00
Liang Ding c065239e69
🎨 #3 2020-01-18 16:54:18 +08:00
Liang Ding 7d222f22cb
🎨 #3 2020-01-18 16:42:56 +08:00
Liang Ding ab4feaba81
🎨 Docker 环境输出延时问题 2020-01-18 15:40:52 +08:00
Liang Ding aa65a8db04
🎨 Docker 环境输出延时问题 2020-01-18 14:38:56 +08:00
Liang Ding e4f0e410b8
⬆️ 更新依赖 2020-01-18 14:18:08 +08:00
Van 0568356fdc
login ui 2019-12-14 17:10:40 +08:00
Liang Ding 215820f02b
🎨 #2 2019-12-14 16:32:28 +08:00
Liang Ding c3b7c26119
🎨 #2 2019-12-13 20:54:55 +08:00
Liang Ding 89e3b7be8f
🎨 #2 2019-12-13 20:50:35 +08:00
Liang Ding 358067ea10
🎨 #2 2019-12-13 20:29:55 +08:00
Liang Ding bd54c4ef10
🎨 Fix #1 2019-12-11 11:30:38 +08:00
Liang Ding 47374dd8c8
🎨 重新导入 2019-12-01 20:21:00 +08:00
Liang Ding e84f994f2b
📝 更新说明 2019-10-07 20:18:59 +08:00
Liang Ding 90303f2aed
📝 更新说明 2019-09-23 22:32:07 +08:00
Liang Ding f633ca07eb
📝 更新说明 2019-09-23 22:30:41 +08:00
Liang Ding a6f9de5a62
⬆️ 更新依赖 2019-08-06 12:04:27 +08:00
Van d26f35bc5f
⬆️ lodash.template 2019-07-18 09:00:38 +08:00
Van 5f0612530a
❇️ add sponsor 2019-07-17 18:36:28 +08:00
Van e281d0f554
❇️ add sponsor 2019-07-17 18:35:00 +08:00
Van dd949ae6a3
add sponsor 2019-07-17 18:15:59 +08:00
Liang Ding 6d8a334131
Merge branch '1.7.0-dev' 2019-07-07 18:23:05 +08:00
Liang Ding 3cf3063e76
:octocat: 关注项目并邀请加入 B3 开源组织 2019-07-07 18:22:54 +08:00
Van fbb824de6e
min css -> clear css 2019-06-23 15:27:15 +08:00
Van ef52c48b26
fix #367 2019-06-23 14:45:20 +08:00
Van 1c295785d0
rm log 2019-06-23 14:14:37 +08:00
D b57889bdac
Merge pull request #366 from b3log/dependabot/npm_and_yarn/braces-2.3.2
⬆️ Bump braces from 1.8.5 to 2.3.2
2019-06-07 19:23:30 +08:00
dependabot[bot] 9d0113f979
⬆️ Bump braces from 1.8.5 to 2.3.2
Bumps [braces](https://github.com/micromatch/braces) from 1.8.5 to 2.3.2.
- [Release notes](https://github.com/micromatch/braces/releases)
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/commits)

Signed-off-by: dependabot[bot] <support@github.com>
2019-06-07 11:18:18 +00:00
Van 7a254f89d8
Upgrade concat-with-sourcemaps to version 1.0.6 or later 2019-05-30 16:41:02 +08:00
Liang Ding 544cb886ec
:octocat: 使用组织库提供的模板 2019-05-30 09:58:27 +08:00
Liang Ding cdc1f5408b
🎨 交叉编译支持 2019-05-29 18:48:08 +08:00
Liang Ding f6bdc5f176
🎨 更新依赖 2019-05-29 18:47:48 +08:00
Liang Ding d3ab996830
🎨 使用组织库默认仓库配置 2019-05-25 23:19:16 +08:00
Liang Ding bae3ad8c20
:octocat: 加入社区协作相关文档 2019-05-25 22:54:20 +08:00
Liang Ding 7dafee49e9
💝 加入赞助链接 2019-05-25 22:53:58 +08:00
Liang Ding efce7adc51
📝 更新徽标 2019-05-25 22:05:46 +08:00
Liang Ding d474e913f6
💚 移除测试覆盖率 2019-05-25 00:07:32 +08:00
Liang Ding c5fdd3f0a6
📝 更新 README 2019-05-24 23:46:30 +08:00
Liang Ding 6dc68d4aa5
:octocat: 主要语言确实是 js 😂 2019-05-24 23:40:49 +08:00
Liang Ding c0c00bf9c8
:octocat: 测试忽略 js 语言 2019-05-24 23:39:00 +08:00
Liang Ding 4f9221c313
:octocat: 更新项目主要语言 2019-05-24 23:35:06 +08:00
Liang Ding f3e9502443
:octocat: 忽略 min 压缩 js 2019-05-24 23:30:53 +08:00
Liang Ding 00439ae418
:octocat: 改变项目主要语言为 Go 2019-05-24 23:28:58 +08:00
Liang Ding c931e611db
🔥 删除单元测试临时目录 2019-05-24 22:36:20 +08:00
Liang Ding b53d2d28b7
♻️ 重构返回值状态码 2019-05-24 21:52:17 +08:00
Liang Ding 83d52b107a
📝 引入 Gulu 工具库 2019-05-24 21:06:00 +08:00
Liang Ding 4930408b17
♻️ 引入 Gulu 工具库 2019-05-24 21:04:25 +08:00
Liang Ding 1405d7482a
💬 删除 README 中的废话 2019-05-24 16:16:32 +08:00
Liang Ding 0253ad8e45
✏️ 修复笔误 2019-05-24 11:54:00 +08:00
Liang Ding 4da07183a9
🔖 更新版本号 2019-05-24 11:53:30 +08:00
Liang Ding 1eb2a78c3b
🔧 默认生产模式 2019-05-24 11:45:17 +08:00
Liang Ding 6bd4efd16f
🔧 默认生成模式 2019-05-24 11:44:58 +08:00
Liang Ding 17e59cf55a
🎨 判断目录 2019-05-24 11:43:26 +08:00
Liang Ding 879b3d78c3
✏️ Fix typos 2019-05-24 11:42:52 +08:00
Liang Ding ce8351f38c
🎨 #363 2019-05-24 11:36:42 +08:00
Liang Ding 58ae60da85
🎨 #363 2019-05-24 08:43:07 +08:00
Liang Ding 5c549a56c5
🎨 #363 2019-05-24 08:41:34 +08:00
Liang Ding 38f88c2c17
🎨 Fix #362 2019-05-23 18:42:59 +08:00
Liang Ding 1857df995d
#362 2019-05-23 18:28:11 +08:00
Liang Ding 67c36e0089
🎨 更新 Playground 示例 2019-05-23 15:51:56 +08:00
Liang Ding d764f13192
📝 更新 Hello World 示例 2019-05-23 15:30:38 +08:00
Liang Ding 9716c3027f
🎨 Fix #360 2019-05-23 11:46:01 +08:00
Liang Ding e2c0b8ec8a
🎨 #360 2019-05-23 11:26:33 +08:00
Liang Ding 2e0f150cdb
🎨 Fix #361 2019-05-23 11:22:42 +08:00
Liang Ding 4a28d10a9d
🎨 #306 goproxy 2019-05-20 17:59:10 +08:00
Liang Ding eb896e3db3
🎨 #306 GO111MODULE=on 2019-05-20 17:56:58 +08:00
Liang Ding d3f522acd9
#306 支持 go mod 2019-05-20 17:36:16 +08:00
Liang Ding 00051a9e39
🎨 #306 go get -> go mod 2019-05-20 17:12:13 +08:00
Liang Ding b1194b4763
🔧 移除 CPU 核数设置 2019-05-19 21:03:32 +08:00
Liang Ding ffd99ff8bd
🌐 默认使用中文 2019-05-19 20:47:13 +08:00
Liang Ding c7372868e4
🌐 更新 title 2019-05-19 20:42:25 +08:00
Van c86f0daa94
🐛 playground output 2019-05-19 18:58:33 +08:00
Liang Ding 03cbb8fce4
🎨 升级公告 2019-05-19 15:37:37 +08:00
Liang Ding 763e140775
🎨 进程运行结束信号 2019-05-19 11:01:36 +08:00
Liang Ding 2b50910784
📦 打包 2019-05-19 01:54:05 +08:00
Liang Ding 72a7281513
🎨 playground 输出 2019-05-19 01:53:31 +08:00
Liang Ding 7576910f43
🐳 限制 CPU 使用 2019-05-19 01:29:27 +08:00
Liang Ding ff3546a844
🎨 kill 命令日志 2019-05-19 01:25:41 +08:00
Liang Ding b84338bb87
♻️ 重构进程管理 2019-05-19 01:20:47 +08:00
Liang Ding f8d26e0c57
🎨 通过 docker rm 回收容器 2019-05-19 01:03:33 +08:00
Liang Ding 02a809803e
🐳 通过 docker rm 回收容器 2019-05-19 00:59:10 +08:00
Liang Ding 9a16c9214f
🎨 不通过 timeout 命令控制执行超时 2019-05-19 00:45:49 +08:00
Liang Ding e952c8fa09
🐳 执行超时控制 2019-05-18 23:38:49 +08:00
Van a236fc195c
Merge remote-tracking branch 'origin/master' 2019-05-17 21:37:30 +08:00
Van 1787db9091
:pack: 2019-05-17 21:37:18 +08:00
Liang Ding 802178cbf3
🐳 仅限制 CPU 2019-05-17 18:27:39 +08:00
Liang Ding 43a3ffe2a0
🐳 限制 CPU、内存使用 2019-05-17 18:20:36 +08:00
Liang Ding 04a2c8ed2f
🎨 登录验证 2019-05-17 16:35:47 +08:00
Liang Ding 1d802f8717
🚚 重命名文件 2019-05-17 16:30:39 +08:00
Liang Ding e230dfdb2d
🎨 登录跳转 2019-05-17 14:09:19 +08:00
Van fb124a3a5b
🐛 2019-05-17 13:50:15 +08:00
Liang Ding 76dd5cacba
🎨 登录页 2019-05-17 13:26:02 +08:00
Liang Ding 4a5f8a1314
🎨 社区地址 2019-05-17 13:22:34 +08:00
Liang Ding 8341881400
🔥 删掉短网址功能 2019-05-17 13:18:33 +08:00
Liang Ding f764d61323
🎨 沙盒短网址 2019-05-17 13:14:46 +08:00
Liang Ding f32e0a21d3
🎨 ws 协议 2019-05-17 13:04:56 +08:00
Liang Ding 0d5f5f80f8
🎨 文件目录 2019-05-17 13:00:59 +08:00
Liang Ding 4760796915
🎨 监听端口 2019-05-17 12:42:16 +08:00
Liang Ding 58de20eb86
🔧 默认定时打印运行报告 2019-05-17 12:37:10 +08:00
Liang Ding 7d7e51e8a6
🎨 登录页 2019-05-17 12:20:38 +08:00
Liang Ding ad6df5b1b4
🎨 #347 2019-05-17 12:17:58 +08:00
Van 4b6f6af949
Merge remote-tracking branch 'origin/master' 2019-05-17 12:02:47 +08:00
Van 6bdd404469
🎨 login 2019-05-17 12:02:35 +08:00
Liang Ding 72e46e3e52
🎨 登录重定向 2019-05-17 11:59:57 +08:00
Liang Ding 29d4d93019
🔇 减少日志 2019-05-17 11:57:15 +08:00
Liang Ding 359ce01227
📝 更新 README 2019-05-17 11:45:19 +08:00
Liang Ding beb58995af
Merge branch 'master' of https://github.com/b3log/wide 2019-05-17 11:41:30 +08:00
Liang Ding 7e62a1db3f
📝 更新 README 2019-05-17 11:41:26 +08:00
Liang Ding 8c520527ce
📝 更新 README 2019-05-17 11:40:12 +08:00
Van bc1f8a5853
Merge remote-tracking branch 'origin/master' 2019-05-17 11:29:27 +08:00
Van ade8fa8892
🎨 login 2019-05-17 11:29:15 +08:00
Liang Ding ec22992396
📄 更新文件头 2019-05-17 11:28:50 +08:00
Liang Ding d0396da439
🎨 #347 2019-05-17 11:25:44 +08:00
Liang Ding 8b0b1503c3
Merge branch 'master' of https://github.com/b3log/wide 2019-05-17 10:39:21 +08:00
Liang Ding b8507efed3
🎨 #347 2019-05-17 10:34:09 +08:00
Van c8a82c1694
🎨 2019-05-17 10:26:00 +08:00
Van 283270c4e1
🎨 2019-05-17 10:25:11 +08:00
Van 7167b83c9e
Merge remote-tracking branch 'origin/master'
# Conflicts:
#	package-lock.json
2019-05-17 09:48:47 +08:00
Van 4d16aac364
🎨 add 2019-05-17 09:48:30 +08:00
Liang Ding e7a3fa9334
🎨 #347 2019-05-17 09:32:03 +08:00
Liang Ding eabce6b343
🎨 #347 2019-05-17 09:27:22 +08:00
Liang Ding aa17c25400
🎨 #347 2019-05-17 01:41:04 +08:00
Liang Ding 4f48e827f6
🎨 #347 2019-05-17 01:06:01 +08:00
Liang Ding be63fc0d16
🔧 简化配置 2019-05-17 00:49:13 +08:00
Liang Ding 5989d0fb7b
🎨 #347 2019-05-17 00:41:52 +08:00
Liang Ding 003354ffe4
🎨 #347 2019-05-16 23:37:04 +08:00
Liang Ding b5aa4f9a7d
🏗️ #347 2019-05-16 23:17:25 +08:00
Liang Ding 912c5821f1
📄 更新授权细节 2019-05-16 21:44:13 +08:00
Liang Ding 2cbf74ddf7
📝 更新文档地址 2019-05-16 21:41:20 +08:00
Liang Ding cc928edb4a
🔥 Fix #357 2019-05-16 21:28:55 +08:00
Liang Ding 44aee6b548
🔥 删除 doc 2019-05-16 21:28:31 +08:00
Liang Ding ec89d3ee4e
🔥 #356 2019-05-16 21:21:26 +08:00
Liang Ding b115e34f15
🎨 Fix #318 2019-05-16 18:59:39 +08:00
Liang Ding f32bc7ab5f
🎨 #296 2019-05-16 18:53:05 +08:00
Liang Ding ad5ffb7510
🎨 #296 2019-05-16 18:42:47 +08:00
Liang Ding 7f28661cb4
🎨 非 Windows 环境强制使用 docker 2019-05-16 18:29:28 +08:00
Liang Ding efa278c773
🔥 删除 Shell 功能 2019-05-16 18:26:32 +08:00
Liang Ding c24e4f4711
🎨 #318 2019-05-16 18:12:38 +08:00
Liang Ding 7deb7c60e5
🎨 #318 2019-05-16 18:10:44 +08:00
Liang Ding e97ce75b92
🐛 NPE 2019-05-16 17:50:58 +08:00
Liang Ding b97ba481ef
🔥 #356 2019-05-16 17:36:27 +08:00
Liang Ding c13b585d3a
🎨 #318 2019-05-16 17:27:43 +08:00
Liang Ding 1c3c96e371
🔒 #355 删除上传文件 2019-05-16 17:25:47 +08:00
Liang Ding 73f873c62f
💚 使用 go 1.12 进行 CI 2019-05-16 13:55:06 +08:00
Liang Ding c62d40e3a9
🎨 #296 2019-05-16 13:52:18 +08:00
Liang Ding 93a64bb9d7
🎨 #296 2019-05-16 13:51:18 +08:00
Liang Ding 8058946f14
🎨 执行超时反馈 2019-05-16 13:46:53 +08:00
Liang Ding 0d36d56631
. 2019-05-16 13:18:09 +08:00
Liang Ding 9a3316b124
🎨 进程状态 2019-05-16 13:12:04 +08:00
Liang Ding 51fa9146d1
🎨 进程退出状态 2019-05-16 13:07:28 +08:00
Liang Ding c80d6746c0
🎨 Cleanup code 2019-05-16 12:56:55 +08:00
Liang Ding c7e4034407
🔒 #296 限制执行时间 2019-05-16 12:56:25 +08:00
Liang Ding 6bc8f32e85
🐳 沙箱环境 2019-05-16 12:48:44 +08:00
Liang Ding 41e2316cc5
🎨 docker 沙箱 2019-05-16 12:47:03 +08:00
Liang Ding be2d5b74b3
🎨 user cache dir 2019-05-16 12:41:39 +08:00
Liang Ding 07e9f4654f
🎨 Cleanup code 2019-05-16 12:19:26 +08:00
Liang Ding 318f453125
🎨 用户目录 2019-05-16 12:13:11 +08:00
Liang Ding f1cc112a52
🎨 读取用户目录 2019-05-16 12:07:21 +08:00
Liang Ding bb3d4979b7
🎨 Cleanup code 2019-05-16 12:02:32 +08:00
Liang Ding d30e3b76b9
🎨 Cleanup code 2019-05-16 11:44:07 +08:00
Liang Ding 1d84db7740
🔧 用户元数据目录配置化 2019-05-16 11:17:06 +08:00
Liang Ding 1a4feddf34
🐳 #296 2019-05-16 10:53:22 +08:00
Liang Ding 88f16e1057
🎨 修复编译问题 2019-05-16 10:53:03 +08:00
Liang Ding efcceccac4
🎨 Cleanup code 2019-05-16 10:52:31 +08:00
Liang Ding 11dbe42c9a
使用模块依赖 2019-05-16 09:52:21 +08:00
Liang Ding a8121d05f8
🐳 #296 去掉命名空间 2019-05-16 09:51:58 +08:00
Liang Ding 1d42fb1215
去掉 vendor 2019-05-16 09:47:09 +08:00
Liang Ding 6cdd14c783
🎉 📄 2019
2019 新年快乐!
2019-01-02 21:43:11 +08:00
Liang Ding f86ca5b632
💡 增加注释 2018-12-30 14:57:43 +08:00
D ce2ae8f97f
Fix #352
添加编译工具依赖的动态库环境变量
2018-12-30 14:55:39 +08:00
yougg 6b4879fcbd
添加编译工具依赖的动态库环境变量 2018-12-30 14:26:39 +08:00
Liang Ding 60b3de2159
💚 修复单元测试 2018-10-28 10:07:01 +08:00
Liang Ding 2edbefd883
🐛 Fix #349 2018-10-28 10:03:24 +08:00
Liang Ding d4980a0e4d
🐛 Fix #349 2018-10-28 09:59:23 +08:00
D 10d8d65d72
Merge pull request #348 from HaraldNordgren/go_versions
Bump Go versions and use 1.n.x to get latest minor versions
2018-10-28 09:30:28 +08:00
Harald Nordgren 138583d96f Bump Go versions and use 1.n.x to get latest minor versions 2018-10-28 00:05:43 +02:00
Liang Ding 22670dcd7f
:octocat: 加入 GitHub Issue 模板 2018-10-07 09:57:12 +08:00
Liang Ding 36f0921c55
📝 README 使用中文 2018-10-07 09:55:34 +08:00
Liang Ding e23e26dbdf
📝 更新用户指南 2018-10-07 08:53:18 +08:00
Van 3666c12738
⬆️ upgrade gulp 2018-10-05 23:11:52 +08:00
Van 50b03b1ec8
:sparklers: fixed #346 2018-10-05 21:04:40 +08:00
Liang Ding fb9a273af1
🎨 跳过常规库目录扫描 2018-10-05 20:52:11 +08:00
Van 87c528eac3
:sparklers: 2018-10-05 17:29:18 +08:00
Liang Ding 8c7d2636b2
🔧 Change repo language 2018-05-06 22:33:44 +08:00
Liang Ding e39b302bdc Update vendor 2018-03-13 13:24:04 +08:00
Liang Ding 982ef9c07f Update vendor 2018-03-13 13:19:03 +08:00
Liang Ding 0b8da63175 Update vendor 2018-03-13 13:11:10 +08:00
Liang Ding 0a51a03f35 Update gocode in vendor 2018-03-13 13:05:45 +08:00
Liang Ding bfd836c6fc 🐳 docker build 2018-03-13 12:57:02 +08:00
Liang Ding 1d1b548a68 🐳 docker build 2018-03-13 12:32:44 +08:00
Liang Ding 1fcc40af30 💚 CI 2018-03-13 12:18:17 +08:00
Liang Ding b80a132866 💚 CI 2018-03-13 12:15:51 +08:00
Liang Ding aea0392beb 🔧 Vendoring 2018-03-13 12:10:52 +08:00
Liang Ding f96c8befdf 🔧 vendor 2018-03-13 12:09:30 +08:00
Liang Ding 01ccdc329a 🐳 docker build 2018-03-13 12:06:55 +08:00
Liang Ding 688cb7cce4 🐳 docker build 2018-03-13 12:04:39 +08:00
Liang Ding 3f48fd3250 🐳 docker build 2018-03-13 11:58:24 +08:00
Liang Ding 7dc9e0e867 🐳 Docker build 2018-03-13 11:54:01 +08:00
Liang Ding 7b040f9f4f Add vendor 2018-03-13 11:47:32 +08:00
Liang Ding c023c9ae8c Add vendor 2018-03-13 11:29:05 +08:00
Liang Ding a399713787 🐳 Update Dockerfile 2018-03-13 11:25:58 +08:00
Liang Ding 9f067a8361 🔥 Remove redundant dependencies 2018-03-13 11:25:13 +08:00
Liang Ding 3548f97239 🔧 Update author email 2018-03-13 10:58:49 +08:00
Liang Ding eed97d5fd8 🎨 Fix #334 2018-03-13 10:53:05 +08:00
Liang Ding 8229848549 🔒 Update external URL using SSL 2018-03-12 12:28:33 +08:00
Liang Ding b7f209abcc 🔧 Update author email 2018-03-12 12:20:52 +08:00
Liang Ding eeeacf917c 🍱 Go Report Card 2018-02-20 12:47:19 +08:00
Liang Ding d905075fc1 📄 2018 Happy new year! 2018-01-01 12:35:13 +08:00
Liang Ding fc36272dc8 🔧 File header exclude vendor 2017-11-14 19:40:26 +08:00
Liang Ding ae678d84d0 📄 Add file header 2017-11-14 19:40:07 +08:00
Liang Ding f859a19e7b 💚 Fix CI build 2017-10-18 13:29:22 +08:00
Liang Ding 5121acdc58 ⬆️ #326 2017-08-15 11:02:32 +08:00
Liang Ding 0b0a67cc33 🔖 Release 1.5.3 2017-08-09 09:39:16 +08:00
Liang Ding 197a33f0e8 🐛 Fix #281 2017-08-09 09:19:36 +08:00
Liang Ding 02301d15fb 🎨 Fix #325 2017-08-09 00:35:20 +08:00
Liang Ding 1fb29e4602 🐳 #325 2017-08-08 22:00:00 +08:00
Liang Ding 2108a9b407 🎨 Fix #322 2017-08-08 21:59:40 +08:00
Liang Ding eba96fd0b6 🐳 #325 2017-08-08 21:21:47 +08:00
Liang Ding 8e972abee0 Merge branch 'master' into 1.5.3-dev 2017-08-08 21:01:50 +08:00
Liang Ding 26548d8717 🎨 Run client program using 'runner' user in docker container 2017-08-08 21:00:49 +08:00
Liang Ding d919da01a7 Merge remote-tracking branch 'refs/remotes/origin/1.5.3-dev' 2017-05-05 13:36:23 +08:00
Liang Ding 2895959529 Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-05-05 13:35:52 +08:00
Liang Ding 65e65e2d8a 🎨 Fix #321 Rename user's getters 2017-05-05 13:35:09 +08:00
Liang Ding 41d641224e Merge remote-tracking branch 'refs/remotes/origin/1.5.3-dev' 2017-05-04 18:06:10 +08:00
Liang Ding 4893c974bc Fix #320 2017-05-04 18:05:01 +08:00
Liang Ding 81f3bce1c5 🔧 Change admin user default theme to 'default' 2017-05-04 17:21:50 +08:00
Van e456a17443 :sparklers: upgrade gulp cc 2017-04-24 00:11:51 +08:00
Van 0e6495cb66 :sparklers: add study link 2017-04-24 00:10:16 +08:00
Liang Ding 2b790ce300 Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-03-29 21:06:23 +08:00
Liang Ding a000f6b44c Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-03-29 21:05:58 +08:00
D 8ffe989c2e Merge pull request #317 from khjde1207/master
修改不能显示双引号的问题。
2017-03-29 21:05:21 +08:00
khjde1207 f69d7c3f06 Update users.go 2017-03-29 13:56:57 +09:00
khjde1207 687c953931 " -> &quot;
-ldflags "-H windowsgui" 중 " 이후 부분 잘리는 현상 해결.
2017-03-29 13:37:09 +09:00
Liang Ding 3cb76438ff Merge remote-tracking branch 'refs/remotes/origin/1.5.3-dev' 2017-03-27 21:32:18 +08:00
Liang Ding 4ab1cb3293 🎨 Some little tweaks for #308
@khjde1207
2017-03-27 21:31:35 +08:00
Liang Ding ec3c3b52f4 Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-03-27 21:08:40 +08:00
Liang Ding b61764ef37 Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-03-27 21:07:24 +08:00
D f4fec2978e Merge pull request #316 from khjde1207/master
Enhance #308 by specifying OS.
2017-03-27 21:04:07 +08:00
khjde1207 dfcbeaa9c8 Update build.go 2017-03-27 11:52:32 +09:00
khjde1207 bc9466f435 Update cross.go 2017-03-27 11:51:55 +09:00
khjde1207 b5365794eb Update cross.go 2017-03-27 11:42:10 +09:00
khjde1207 d7550b110a Update user.go 2017-03-27 11:22:22 +09:00
khjde1207 2293e37e72 Update cross.go 2017-03-27 11:05:31 +09:00
khjde1207 ed7170a02f Update build.go 2017-03-27 11:04:34 +09:00
khjde1207 4b1cdbb249 Update wide.go 2017-03-27 11:00:15 +09:00
khjde1207 b33638dc06 Update user.go 2017-03-27 10:59:35 +09:00
khjde1207 e1dca8c16d Update menu.js 2017-03-27 10:58:05 +09:00
khjde1207 9314f2b721 Update wide.min.js.map 2017-03-27 10:46:50 +09:00
khjde1207 290625c8e9 Update wide.min.js 2017-03-27 10:43:41 +09:00
khjde1207 2f4ead4731 Update users.go 2017-03-27 10:38:59 +09:00
khjde1207 699ba5e0c5 Update preference.html 2017-03-27 10:37:28 +09:00
Liang Ding d1a54532c9 Merge remote-tracking branch 'refs/remotes/origin/1.5.3-dev' 2017-03-15 23:38:56 +08:00
Liang Ding ee069e75ac 🎨 logging 2017-03-15 23:38:34 +08:00
Liang Ding 0309fd01a2 Merge remote-tracking branch 'refs/remotes/origin/master' into 1.5.3-dev 2017-03-15 23:37:54 +08:00
Liang Ding e4e2c029e1 🎨 Compress static resource 2017-03-15 23:37:52 +08:00
Liang Ding 4a4788be85 Merge remote-tracking branch 'refs/remotes/origin/1.5.3-dev' 2017-03-15 23:27:06 +08:00
Liang Ding ea40ff990b Fix #308
解决构建报错时 lint 渲染以及运行输出渲染换行的问题
2017-03-15 23:26:10 +08:00
Liang Ding 029bc7c917 🔨 #308
重新调整了构建时输出流和错误流的处理
TBD:构建出错时的 lints 结构组装
2017-03-15 22:26:12 +08:00
Liang Ding a89af9a374 🔧 Tweak default user confs 2017-03-15 22:24:32 +08:00
Liang Ding a93a52197d 🏁 Fix output's path jump failed on Windows 2017-03-15 22:23:23 +08:00
Liang Ding bfbc47c3c4 #308
用户可以在 Preference->Go Tool 中设置构建参数。

需要重写 build 输出处理,目前是按照是否有输出来判断构建成败,应该改为按输出流/错误流判断
2017-03-15 00:21:13 +08:00
Liang Ding 130006608f 📄 Update copyright year 2017-03-14 22:40:45 +08:00
Liang Ding d5493ea380 🐳 Dockerfile 2017-01-23 20:19:12 +08:00
Liang Ding b7eb57ac99 ⬆️ Upgrade gocode for Docker 2017-01-23 20:04:09 +08:00
Liang Ding 7f4df9b4de Fix #302 2017-01-23 17:49:43 +08:00
Liang Ding d82fa32715 🐳 #302 2017-01-23 16:42:32 +08:00
Liang Ding 9500ea33a2 🐳 #302 2017-01-23 15:28:34 +08:00
Liang Ding 771dffc7d4 🐳 #302
First try
2017-01-23 14:52:29 +08:00
Liang Ding e074ddd235 🎨 format code 2016-12-28 11:17:32 +08:00
D b5453f02b7 Merge pull request #301 from khjde1207/master
反复存档会发生runtime.throw
2016-12-28 09:50:29 +08:00
khjde1207 8155c94ffc 反复存档会发生runtime.throw
反复存档会发生runtime.throw
读了一次就不再读。。。。
2016-12-27 22:50:32 +09:00
D bed29d8418 Update README.md 2016-12-15 10:29:58 +08:00
Liang Ding 0ecb57a871 🔖 Release 1.5.2 2016-12-15 09:20:34 +08:00
Liang Ding 78a44a9744 🎨 clean code 2016-12-14 20:43:34 +08:00
Liang Ding be80e292d0 🎨 format code 2016-12-14 18:19:18 +08:00
Liang Ding 23f299f0bf #293 2016-12-14 18:15:53 +08:00
Van 28d8f99f85 #285 2016-10-30 00:02:21 +08:00
Van 79984ab9ac fixed #285 2016-10-29 23:30:09 +08:00
D 817c556df4 Merge pull request #289 from khjde1207/master
Well done, thank u!
2016-10-25 15:45:32 +08:00
khjde1207 0c42e682a6 Update index.html 2016-10-25 16:16:46 +09:00
D 3f888d0884 Fix #287
非常感谢你!
2016-10-25 14:03:08 +08:00
khjde1207 0686ef3020 ko_KR.json
韩文版
2016-10-25 14:37:07 +09:00
Liang Ding def80b904f Release 1.5.1 2016-08-31 09:26:50 +08:00
D 910cf9c536 Update README.md 2016-06-14 20:25:51 +08:00
Liang Ding 0062c28edb #277 2016-06-12 18:17:36 +08:00
Liang Ding 487d97d844 Fix #277 2016-06-12 17:58:53 +08:00
我叫 D,你好 ;-p 6b4ec1b924 Update README.md 2016-05-07 11:12:30 +08:00
我叫 D,你好 ;-p bcbc6048c4 Merge pull request #274 from shiyou0130011/language-dictionary-fix
修正繁體中文、日文的用詞
2016-05-04 20:12:12 -05:00
Tzuyang Tsai 38f1b190f1 修正日語用詞 2016-05-05 00:00:27 +08:00
Tzuyang Tsai 6d9e4aaf6a 再次修正用詞
修正 commit:714dd8768f5f720c4553a3c5c2e5bf5a03c5b59d 沒修到的

此外,將所有「文件」改為「檔案」、
把不必翻譯成中文的除蟲改為 debug
2016-05-04 23:53:50 +08:00
Tzuyang Tsai 714dd8768f 修改繁體中文的字典
將用詞改為台灣的用詞
2016-05-04 23:41:18 +08:00
Van 5963f23d33 cc 2016-02-27 11:16:04 +08:00
Liang Ding be04f26b0d Fix #268 2016-02-26 13:45:54 +08:00
Liang Ding ada7da92c9 Fix #267
@plinyGo
2016-02-26 11:12:33 +08:00
Van 35576c42b5 compress 2016-02-20 14:44:46 +08:00
Liang Ding f1e1c1e085 黑客派 link update
https://hacpai.com
2016-02-20 14:38:16 +08:00
Liang Ding 952daeb76c Fix undefined layout bug 2016-02-16 21:24:37 +08:00
136 changed files with 7025 additions and 6569 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
static/js/lib/* linguist-vendored
static/js/overwrite/* linguist-vendored
*.min.js linguist-vendored

4
.gitignore vendored
View File

@ -1,8 +1,6 @@
/wide.exe
/wide
/static/user/admin/style.css
/header
/header.exe
@ -10,3 +8,5 @@
/node_modules
/nbproject
/workspaces
.idea

View File

@ -10,11 +10,12 @@
],
"Excludes": [
"static/js/lib/*",
"static/user/*"
"static/user/*",
"vendor/**"
],
"UseDefaultExcludes": true,
"Properties": {
"Year": "2014-2016",
"Owner": "b3log.org & hacpai.com"
"Year": "2014-present",
"Owner": "b3log.org"
}
}

View File

@ -4,7 +4,7 @@ Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,4 @@
language: go
go:
- 1.4
before_install:
- go get github.com/axw/gocov/gocov
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- ./coverage.sh
- $HOME/gopath/bin/goveralls -coverprofile=profile.cov -service=travis-ci -repotoken W04Y4p452CDeI9gcsC8t5y217uHnUDFCb
- 1.12

View File

@ -1,23 +0,0 @@
FROM golang:cross
MAINTAINER Liang Ding <dl88250@gmail.com>
ADD . /wide/gogogo/src/github.com/b3log/wide
RUN tar zxf /wide/gogogo/src/github.com/b3log/wide/deps/golang.org.tar.gz -C /wide/gogogo/src/
RUN tar zxf /wide/gogogo/src/github.com/b3log/wide/deps/github.com.tar.gz -C /wide/gogogo/src/
RUN useradd wide && useradd runner
ENV GOROOT /usr/src/go
ENV GOPATH /wide/gogogo
RUN go build github.com/go-fsnotify/fsnotify
RUN go build github.com/gorilla/sessions
RUN go build github.com/gorilla/websocket
RUN go install github.com/visualfc/gotools github.com/nsf/gocode github.com/bradfitz/goimports
WORKDIR /wide/gogogo/src/github.com/b3log/wide
RUN go build -v
EXPOSE 7070

View File

@ -192,7 +192,7 @@
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,

173
README.md
View File

@ -1,171 +1,18 @@
# [Wide](https://github.com/b3log/wide) [![Build Status](https://img.shields.io/travis/b3log/wide.svg?style=flat)](https://travis-ci.org/b3log/wide) [![Coverage Status](https://img.shields.io/coveralls/b3log/wide.svg?style=flat)](https://coveralls.io/r/b3log/wide) [![Apache License](http://img.shields.io/badge/license-apache2-orange.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0) [![API Documentation](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](http://godoc.org/github.com/b3log/wide) [![Download](http://img.shields.io/badge/download-~3.1K-red.svg?style=flat)](http://pan.baidu.com/s/1dD3XwOT)
* [Wide 用户指南](https://ld246.com/article/1538873544275)
* [Wide 开发指南](https://ld246.com/article/1538876422995)
_Have a [try](http://wide.b3log.org/signup) first, then [download](http://pan.baidu.com/s/1dD3XwOT) and setup it on your local area network, enjoy yourself!_
![Overview](https://cloud.githubusercontent.com/assets/873584/5450620/1d51831e-8543-11e4-930b-670871902425.png)
先试试我们搭建好的[在线服务](http://wide.b3log.org/signup),你可以在这里[下载](http://pan.baidu.com/s/1dD3XwOT)并在本地环境运行,然后邀请小伙伴们来玩吧!
![Goto File](https://cloud.githubusercontent.com/assets/873584/5450616/1d495da6-8543-11e4-9285-f9d9c60779ac.png)
> * 关于 Wide 的产品定位,请看[这里](http://hacpai.com/article/1438407961481),并欢迎参与讨论~
> * 加入[**黑客派**](http://hacpai.com/register),与其他程序员、设计师共同成长!
![Autocomplete](https://cloud.githubusercontent.com/assets/873584/5450619/1d4d5712-8543-11e4-8fe4-35dbc8348a6e.png)
## Introduction
![Theme](https://cloud.githubusercontent.com/assets/873584/5450617/1d4c0826-8543-11e4-8b86-f79a4e41550a.png)
A <b>W</b>eb-based <b>IDE</b> for Teams using Go programming language/Golang.
![Show Expression Info](https://cloud.githubusercontent.com/assets/873584/5450618/1d4cd9f4-8543-11e4-950f-121bd3ff4a39.png)
![Hello, 世界](https://cloud.githubusercontent.com/assets/873584/4606377/d0ca3c2a-521b-11e4-912c-d955ab05850b.png)
![Build Error Info](https://cloud.githubusercontent.com/assets/873584/5450632/3e51cccc-8543-11e4-8ca8-8d2427aa16b8.png)
## Motivation
![Cross-Compilation](https://cloud.githubusercontent.com/assets/873584/10130037/226d75fc-65f7-11e5-94e4-25ee579ca175.png)
* **Team** IDE:
* _Safe and reliable_: the project source code stored on the server in real time, the developer's machine crashes without losing any source code
* _Unified environment_: server unified development environment configuration, the developer machine without any additional configuration
* _Out of the box_: 5 minutes to setup a server then open browser to develop, debug
* _Version Control_: each developer has its own source code repository, easy sync with the trunk
* **Web-based** IDE:
* Developer needs a browser only
* Cross-platform, even on mobile devices
* Easy to extend
* Easy to integrate with other systems
* For the geeks
* A try for commercial-open source: versions customized for enterprises, close to their development work flows respectively
* Currently more popular Go IDE has some defects or regrets:
* Text editor (vim/emacs/sublime/Atom, etc.): For the Go newbie is too complex
* Plug-in (goclipse, etc.): the need for the original IDE support, not professional
* LiteIDE: no modern user interface :p
* No team development experience
* There are a few of GO IDEs, and no one developed by Go itself, this is a nice try
## Features
* [X] Code Highlight, Folding: Go/HTML/JavaScript/Markdown etc.
* [X] Autocomplete: Go/HTML etc.
* [X] Format: Go/HTML/JSON etc.
* [X] Build & Run
* [X] Multiplayer: a real team development experience
* [X] Navigation, Jump to declaration, Find usages, File search etc.
* [X] Shell: run command on the server
* [X] Web development: HTML/JS/CSS editor with [Emmet](http://emmet.io) integrated
* [X] Go tool: go get/install/fmt etc.
* [X] File Import & Export
* [X] Themes: editor and UI adjust, respectively
* [X] Cross-Compilation
* [ ] Debug
* [ ] Git integration: git command on the web
## Screenshots
* **Overview**
![Overview](https://cloud.githubusercontent.com/assets/873584/5450620/1d51831e-8543-11e4-930b-670871902425.png)
* **Goto File**
![Goto File](https://cloud.githubusercontent.com/assets/873584/5450616/1d495da6-8543-11e4-9285-f9d9c60779ac.png)
* **Autocomplete**
![Autocomplete](https://cloud.githubusercontent.com/assets/873584/5450619/1d4d5712-8543-11e4-8fe4-35dbc8348a6e.png)
* **Theme**
![4](https://cloud.githubusercontent.com/assets/873584/5450617/1d4c0826-8543-11e4-8b86-f79a4e41550a.png)
* **Show Expression Info**
![Show Expression Info](https://cloud.githubusercontent.com/assets/873584/5450618/1d4cd9f4-8543-11e4-950f-121bd3ff4a39.png)
* **Build Error Info**
![Build Error Info](https://cloud.githubusercontent.com/assets/873584/5450632/3e51cccc-8543-11e4-8ca8-8d2427aa16b8.png)
* **Git Clone**
![Git Clone](https://cloud.githubusercontent.com/assets/873584/6545235/2284f230-c5b7-11e4-985e-7e04367921b1.png)
* **Cross-Compilation**
![Cross-Compilation](https://cloud.githubusercontent.com/assets/873584/10130037/226d75fc-65f7-11e5-94e4-25ee579ca175.png)
## Architecture
### Build & Run
![Build & Run](https://cloud.githubusercontent.com/assets/873584/4389219/3642bc62-43f3-11e4-8d1f-06d7aaf22784.png)
* A browser tab corresponds to a Wide session
* Execution output push via WebSocket
Flow:
1. Browser sends ````Build```` request
2. Server executes ````go build```` command via ````os/exec````<br/>
2.1. Generates a executable file
3. Browser sends ````Run```` request
4. Server executes the file via ````os/exec````<br/>
4.1. A running process<br/>
4.2. Execution output push via WebSocket channel
5. Browser renders with callback function ````ws.onmessage````
### Code Assist
![Code Assist](https://cloud.githubusercontent.com/assets/873584/4399135/3b80c21c-4463-11e4-8e94-7f7e8d12a4df.png)
* Autocompletion
* Find Usages/Jump To Declaration/etc.
Flow:
1. Browser sends code assist request
2. Handler gets user workspace of the request with HTTP session
3. Server executes ````gocode````/````ide_stub(gotools)````<br/>
3.1 Sets environment variables (e.g. ${GOPATH})<br/>
3.2 ````gocode```` with ````lib-path```` parameter
## Documents
* [用户指南](https://www.gitbook.com/book/88250/wide-user-guide)
* [开发指南](https://www.gitbook.com/book/88250/wide-dev-guide)
## Setup
### Download Binary
We have provided OS-specific executable binary as follows:
* linux-amd64/386
* windows-amd64/386
* darwin-amd64/386
Download [HERE](http://pan.baidu.com/s/1dD3XwOT)!
### Build Wide for yourself
1. [Download](https://github.com/b3log/wide/archive/master.zip) source or by `git clone https://github.com/b3log/wide`
2. Get dependencies with
* `go get`
* `go get github.com/visualfc/gotools github.com/nsf/gocode github.com/bradfitz/goimports`
3. Compile wide with `go build`
### Docker
1. Get image: `sudo docker pull 88250/wide:latest`
2. Run: `sudo docker run -p 127.0.0.1:7070:7070 88250/wide:latest ./wide -docker=true -channel=ws://127.0.0.1:7070`
3. Open browser: http://127.0.0.1:7070
## Known Issues
* [Shell is not available on Windows](https://github.com/b3log/wide/issues/32)
* [Rename directory](https://github.com/b3log/wide/issues/251)
## Terms
* This software is open sourced under the Apache License 2.0
* You can not get rid of the "Powered by [B3log](http://b3log.org)" from any page, even which you made
* If you want to use this software for commercial purpose, please mail to support@liuyun.io for a commercial license request
* Copyright &copy; b3log.org, all rights reserved
## Credits
Wide is made possible by the following open source projects.
* [golang](http://golang.org)
* [CodeMirror](https://github.com/marijnh/CodeMirror)
* [zTree](https://github.com/zTree/zTree_v3)
* [LiteIDE](https://github.com/visualfc/liteide)
* [gocode](https://github.com/nsf/gocode)
* [Gorilla](https://github.com/gorilla)
* [Docker](https://docker.com)
----
<img src="https://cloud.githubusercontent.com/assets/873584/4606328/4e848b96-5219-11e4-8db1-fa12774b57b4.png" width="256px" />
![Playground](https://cloud.githubusercontent.com/assets/873584/21209772/449ecfd2-c2b1-11e6-9aa6-a83477d9f269.gif)

View File

@ -1,4 +1,4 @@
* This software is open sourced under the Apache License 2.0
* You can not get rid of the "Powered by [B3log](http://b3log.org)" from any pages, even the pages are developed by you
* If you want to use this software for commercial purpose, please mail to support@liuyun.io for request a commercial license
* You can not get rid of the "Powered by [B3log](https://b3log.org)" from any pages, even the pages are developed by you
* If you want to use this software for commercial purpose, please mail to os@b3log.org for request a commercial license
* Copyright (c) b3log.org, all rights reserved

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -15,17 +15,13 @@
package conf
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/b3log/wide/util"
)
// Panel represents a UI panel.
@ -51,14 +47,15 @@ type LatestSessionContent struct {
// User configuration.
type User struct {
Id string
Name string
Password string
Salt string
Email string
Gravatar string // see http://gravatar.com
Avatar string
Workspace string // the GOPATH of this user (maybe contain several paths splitted by os.PathListSeparator)
Locale string
GoFormat string
GoBuildArgsForLinux string
GoBuildArgsForWindows string
GoBuildArgsForDarwin string
FontFamily string
FontSize string
Theme string
@ -79,26 +76,7 @@ type editor struct {
TabSize string
}
// NewUser creates a user with the specified username, password, email and workspace.
func NewUser(username, password, email, workspace string) *User {
md5hash := md5.New()
md5hash.Write([]byte(email))
gravatar := hex.EncodeToString(md5hash.Sum(nil))
salt := util.Rand.String(16)
password = Salt(password, salt)
now := time.Now().UnixNano()
return &User{Name: username, Password: password, Salt: salt, Email: email, Gravatar: gravatar, Workspace: workspace,
Locale: Wide.Locale, GoFormat: "gofmt", FontFamily: "Helvetica", FontSize: "13px", Theme: "default",
Keymap: "wide",
Created: now, Updated: now, Lived: now,
Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px",
Theme: "wide", TabSize: "4"}}
}
// Save saves the user's configurations in conf/users/{username}.json.
// Save saves the user's configurations in conf/users/{userId}.json.
func (u *User) Save() bool {
bytes, err := json.MarshalIndent(u, "", " ")
@ -109,12 +87,12 @@ func (u *User) Save() bool {
}
if "" == string(bytes) {
logger.Error("Truncated user [" + u.Name + "]")
logger.Error("Truncated user [" + u.Id + "]")
return false
}
if err = ioutil.WriteFile("conf/users/"+u.Name+".json", bytes, 0644); nil != err {
if err = ioutil.WriteFile(filepath.Join(Wide.Data, "users", u.Id+".json"), bytes, 0644); nil != err {
logger.Error(err)
return false
@ -123,34 +101,62 @@ func (u *User) Save() bool {
return true
}
// GetWorkspace gets workspace path of the user.
// NewUser creates a user with the specified username and workspace.
func NewUser(id, name, avatar, workspace string) *User {
now := time.Now().UnixNano()
return &User{Id: id, Name: name, Avatar: avatar, Workspace: workspace,
Locale: Wide.Locale, GoFormat: "gofmt",
GoBuildArgsForLinux: "", GoBuildArgsForWindows: "", GoBuildArgsForDarwin: "",
FontFamily: "Helvetica", FontSize: "13px", Theme: "default",
Keymap: "wide",
Created: now, Updated: now, Lived: now,
Editor: &editor{FontFamily: "Consolas, 'Courier New', monospace", FontSize: "inherit", LineHeight: "17px",
Theme: "wide", TabSize: "4"}}
}
// WorkspacePath gets workspace path of the user.
//
// Compared to the use of Wide.Workspace, this function will be processed as follows:
// 1. Replace {WD} variable with the actual directory path
// 2. Replace ${GOPATH} with enviorment variable GOPATH
// 3. Replace "/" with "\\" (Windows)
func (u *User) GetWorkspace() string {
w := strings.Replace(u.Workspace, "{WD}", Wide.WD, 1)
func (u *User) WorkspacePath() string {
w := u.Workspace
w = strings.Replace(w, "${GOPATH}", os.Getenv("GOPATH"), 1)
return filepath.FromSlash(w)
}
// BuildArgs get build args with the specified os.
func (u *User) BuildArgs(os string) []string {
var tmp string
if os == "windows" {
tmp = u.GoBuildArgsForWindows
}
if os == "linux" {
tmp = u.GoBuildArgsForLinux
}
if os == "darwin" {
tmp = u.GoBuildArgsForDarwin
}
exp := regexp.MustCompile(`[^\s"']+|"([^"]*)"|'([^']*)'`)
ret := exp.FindAllString(tmp, -1)
for idx := range ret {
ret[idx] = strings.Replace(ret[idx], "\"", "", -1)
}
return ret
}
// GetOwner gets the user the specified path belongs to. Returns "" if not found.
func GetOwner(path string) string {
for _, user := range Users {
if strings.HasPrefix(path, user.GetWorkspace()) {
return user.Name
if strings.HasPrefix(path, user.WorkspacePath()) {
return user.Id
}
}
return ""
}
// Salt salts the specified password with the specified salt.
func Salt(password, salt string) string {
sha1hash := sha1.New()
sha1hash.Write([]byte(password + salt))
return hex.EncodeToString(sha1hash.Sum(nil))
}

View File

@ -1,45 +0,0 @@
{
"Name": "admin",
"Password": "d1bfca21893c908e64fabda01d71294b1ccdcaa7",
"Salt": "dnoyeb",
"Email": "",
"Gravatar": "d41d8cd98f00b204e9800998ecf8427e",
"Workspace": "${GOPATH}",
"Locale": "en_US",
"GoFormat": "gofmt",
"FontFamily": "Helvetica",
"FontSize": "13px",
"Theme": "dark",
"Keymap": "wide",
"Created": 1414080000000000000,
"Updated": 1414080000000000000,
"Lived": 1414080000000000000,
"Editor": {
"FontFamily": "Consolas, 'Courier New', monospace",
"FontSize": "13px",
"LineHeight": "17px",
"Theme": "wide",
"TabSize": "4"
},
"LatestSessionContent": {
"fileTree": [
],
"files": [
],
"currentFile": "",
"layout": {
"side": {
"state": "normal",
"size": 200
},
"sideRight": {
"state": "normal",
"size": 200
},
"bottom": {
"state": "normal",
"size": 100
}
}
}
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -17,19 +17,17 @@ package conf
import (
"encoding/json"
"io/ioutil"
"html/template"
"os"
"os/exec"
"path/filepath"
"sort"
"strconv"
"strings"
"text/template"
"time"
"github.com/b3log/wide/event"
"github.com/b3log/wide/log"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/event"
)
const (
@ -39,42 +37,43 @@ const (
PathListSeparator = string(os.PathListSeparator)
// WideVersion holds the current Wide's version.
WideVersion = "1.5.0"
WideVersion = "1.6.0"
// CodeMirrorVer holds the current editor version.
CodeMirrorVer = "5.1"
// UserAgent represents HTTP client user agent.
UserAgent = "Wide/" + WideVersion + "; +https://github.com/88250/wide"
HelloWorld = `package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
fmt.Println("欢迎通过《边看边练 Go 系列》来学习 Go 语言 https://ld246.com/article/1437497122181")
}
`
)
// Configuration.
type conf struct {
IP string // server ip, ${ip}
Port string // server port
Context string // server context
Server string // server host and port ({IP}:{Port})
StaticServer string // static resources server scheme, host and port (http://{IP}:{Port})
Server string // server
LogLevel string // logging level: trace/debug/info/warn/error
Channel string // channel (ws://{IP}:{Port})
Data string // data directory
RuntimeMode string // runtime mode (dev/prod)
HTTPSessionMaxAge int // HTTP session max age (in seciond)
StaticResourceVersion string // version of static resources
MaxProcs int // Go max procs
RuntimeMode string // runtime mode (dev/prod)
WD string // current working direcitory, ${pwd}
Locale string // default locale
Playground string // playground directory
AllowRegister bool // allow register or not
Autocomplete bool // default autocomplete
SiteStatCode template.HTML // site statistic code
ReadOnly bool // read-only mode
OAuthLoginURL string
OAuthAccessTokenURL string
OAuthUserInfoURL string
OAuthClientID string
OAuthClientSecret string
}
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Wide configurations.
var Wide *conf
@ -82,21 +81,28 @@ var Wide *conf
// configurations of users.
var Users []*User
// Indicates whether runs via Docker.
// Indicates whether Docker is available.
var Docker bool
// Load loads the Wide configurations from wide.json and users' configurations from users/{username}.json.
func Load(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
confPlayground string, confDocker bool) {
// XXX: ugly args list....
// Docker image to run user's program
const DockerImageGo = "golang"
initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
confPlayground, confDocker)
// Load loads the Wide configurations from wide.json and users' configurations from users/{userId}.json.
func Load(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) {
initWide(confPath, confData, confServer, confLogLevel, confReadOnly, confSiteStatCode)
initUsers()
cmd := exec.Command("docker", "version")
_, err := cmd.CombinedOutput()
if nil != err {
logger.Warnf("Not found 'docker' installed, running user's code will cause security problem")
} else {
Docker = true
}
}
func initUsers() {
f, err := os.Open("conf/users")
f, err := os.Open(Wide.Data + PathSeparator + "users")
if nil != err {
logger.Error(err)
@ -116,10 +122,13 @@ func initUsers() {
continue
}
if ".json" != filepath.Ext(name) { // such as backup (*.json~) not be created by Wide
continue
}
user := &User{}
bytes, _ := ioutil.ReadFile("conf/users/" + name)
bytes, _ := os.ReadFile(filepath.Join(Wide.Data, "users", name))
err := json.Unmarshal(bytes, user)
if err != nil {
logger.Errorf("Parses [%s] error: %v, skip loading this user", name, err)
@ -132,6 +141,17 @@ func initUsers() {
user.Keymap = "wide"
}
// Compatibility upgrade (1.5.3): https://github.com/b3log/wide/issues/308
//if "" == user.GoBuildArgsForLinux {
// user.GoBuildArgsForLinux = "-i"
//}
//if "" == user.GoBuildArgsForWindows {
// user.GoBuildArgsForWindows = "-i"
//}
//if "" == user.GoBuildArgsForDarwin {
// user.GoBuildArgsForDarwin = "-i"
//}
Users = append(Users, user)
}
@ -139,9 +159,8 @@ func initUsers() {
initCustomizedConfs()
}
func initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticServer, confContext, confChannel,
confPlayground string, confDocker bool) {
bytes, err := ioutil.ReadFile(confPath)
func initWide(confPath, confData, confServer, confLogLevel, confReadOnly string, confSiteStatCode template.HTML) {
bytes, err := os.ReadFile(confPath)
if nil != err {
logger.Error(err)
@ -157,94 +176,65 @@ func initWide(confPath, confIP, confPort, confServer, confLogLevel, confStaticSe
os.Exit(-1)
}
Wide.Autocomplete = true // default to true
// Logging Level
log.SetLevel(Wide.LogLevel)
gulu.Log.SetLevel(Wide.LogLevel)
if "" != confLogLevel {
Wide.LogLevel = confLogLevel
log.SetLevel(confLogLevel)
gulu.Log.SetLevel(confLogLevel)
}
logger.Debug("Conf: \n" + string(bytes))
// Working Directory
Wide.WD = util.OS.Pwd()
logger.Debugf("${pwd} [%s]", Wide.WD)
// User Home
home, err := util.OS.Home()
home, err := gulu.OS.Home()
if nil != err {
logger.Error("Can't get user's home, please report this issue to developer", err)
os.Exit(-1)
}
logger.Debugf("${user.home} [%s]", home)
// Playground Directory
Wide.Playground = strings.Replace(Wide.Playground, "${home}", home, 1)
if "" != confPlayground {
Wide.Playground = confPlayground
// Data directory
if "" != confData {
Wide.Data = confData
}
if !util.File.IsExist(Wide.Playground) {
if err := os.Mkdir(Wide.Playground, 0775); nil != err {
logger.Errorf("Create Playground [%s] error", err)
Wide.Data = strings.Replace(Wide.Data, "${home}", home, -1)
Wide.Data = filepath.Clean(Wide.Data)
if err := os.MkdirAll(Wide.Data+"/playground/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
os.Exit(-1)
}
}
// IP
if "" != confIP {
Wide.IP = confIP
} else {
ip, err := util.Net.LocalIP()
if nil != err {
logger.Error(err)
if err := os.MkdirAll(Wide.Data+"/users/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
os.Exit(-1)
}
if err := os.MkdirAll(Wide.Data+"/workspaces/", 0755); nil != err {
logger.Errorf("Create data directory [%s] error", err)
logger.Debugf("${ip} [%s]", ip)
Wide.IP = strings.Replace(Wide.IP, "${ip}", ip, 1)
os.Exit(-1)
}
if "" != confPort {
Wide.Port = confPort
}
// Docker flag
Docker = confDocker
// Server
Wide.Server = strings.Replace(Wide.Server, "{IP}", Wide.IP, 1)
Wide.Server = strings.Replace(Wide.Server, "{Port}", Wide.Port, 1)
if "" != confServer {
Wide.Server = confServer
}
// Static Server
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", Wide.IP, 1)
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{Port}", Wide.Port, 1)
if "" != confStaticServer {
Wide.StaticServer = confStaticServer
if "" != confReadOnly {
Wide.ReadOnly, _ = strconv.ParseBool(confReadOnly)
}
// Context
if "" != confContext {
Wide.Context = confContext
// SiteStatCode
if "" != confSiteStatCode {
Wide.SiteStatCode = confSiteStatCode
}
time := strconv.FormatInt(time.Now().UnixNano(), 10)
logger.Debugf("${time} [%s]", time)
Wide.StaticResourceVersion = strings.Replace(Wide.StaticResourceVersion, "${time}", time, 1)
// Channel
Wide.Channel = strings.Replace(Wide.Channel, "{IP}", Wide.IP, 1)
Wide.Channel = strings.Replace(Wide.Channel, "{Port}", Wide.Port, 1)
if "" != confChannel {
Wide.Channel = confChannel
}
}
// FixedTimeCheckEnv checks Wide runtime enviorment periodically (7 minutes).
@ -262,7 +252,7 @@ func FixedTimeCheckEnv() {
}
func checkEnv() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
cmd := exec.Command("go", "version")
buf, err := cmd.CombinedOutput()
@ -279,16 +269,16 @@ func checkEnv() {
os.Exit(-1)
}
gocode := util.Go.GetExecutableInGOBIN("gocode")
gocode := gulu.Go.GetExecutableInGOBIN("gocode")
cmd = exec.Command(gocode)
_, err = cmd.Output()
if nil != err {
event.EventQueue <- &event.Event{Code: event.EvtCodeGocodeNotFound}
logger.Warnf("Not found gocode [%s], please install it with this command: go get github.com/nsf/gocode", gocode)
logger.Warnf("Not found gocode [%s], please install it with this command: go get github.com/stamblerre/gocode", gocode)
}
ideStub := util.Go.GetExecutableInGOBIN("gotools")
ideStub := gulu.Go.GetExecutableInGOBIN("gotools")
cmd = exec.Command(ideStub, "version")
_, err = cmd.Output()
if nil != err {
@ -298,11 +288,11 @@ func checkEnv() {
}
}
// GetUserWorkspace gets workspace path with the specified username, returns "" if not found.
func GetUserWorkspace(username string) string {
// GetUserWorkspace gets workspace path with the specified user id, returns "" if not found.
func GetUserWorkspace(userId string) string {
for _, user := range Users {
if user.Name == username {
return user.GetWorkspace()
if user.Id == userId {
return user.WorkspacePath()
}
}
@ -310,14 +300,14 @@ func GetUserWorkspace(username string) string {
}
// GetGoFmt gets the path of Go format tool, returns "gofmt" if not found "goimports".
func GetGoFmt(username string) string {
func GetGoFmt(userId string) string {
for _, user := range Users {
if user.Name == username {
if user.Id == userId {
switch user.GoFormat {
case "gofmt":
return "gofmt"
case "goimports":
return util.Go.GetExecutableInGOBIN("goimports")
return gulu.Go.GetExecutableInGOBIN("goimports")
default:
logger.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
return "gofmt"
@ -328,15 +318,14 @@ func GetGoFmt(username string) string {
return "gofmt"
}
// GetUser gets configuration of the user specified by the given username, returns nil if not found.
func GetUser(username string) *User {
if "playground" == username { // reserved user for Playground
// mock it
return NewUser("playground", "", "", "")
// GetUser gets configuration of the user specified by the given user id, returns nil if not found.
func GetUser(id string) *User {
if "playground" == id { // reserved user for Playground
return NewUser("playground", "playground", "", "")
}
for _, user := range Users {
if user.Name == username {
if user.Id == id {
return user
}
}
@ -347,17 +336,17 @@ func GetUser(username string) *User {
// initCustomizedConfs initializes the user customized configurations.
func initCustomizedConfs() {
for _, user := range Users {
UpdateCustomizedConf(user.Name)
UpdateCustomizedConf(user.Id)
}
}
// UpdateCustomizedConf creates (if not exists) or updates user customized configuration files.
//
// 1. /static/user/{username}/style.css
func UpdateCustomizedConf(username string) {
// 1. /static/users/{userId}/style.css
func UpdateCustomizedConf(userId string) {
var u *User
for _, user := range Users { // maybe it is a beauty of the trade-off of the another world between design and implementation
if user.Name == username {
if user.Id == userId {
u = user
}
}
@ -375,8 +364,7 @@ func UpdateCustomizedConf(username string) {
os.Exit(-1)
}
wd := util.OS.Pwd()
dir := filepath.Clean(wd + "/static/user/" + u.Name)
dir := filepath.Clean(Wide.Data + "/static/users/" + userId)
if err := os.MkdirAll(dir, 0755); nil != err {
logger.Error(err)
@ -406,7 +394,7 @@ func initWorkspaceDirs() {
paths := []string{}
for _, user := range Users {
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
paths = append(paths, filepath.SplitList(user.WorkspacePath())...)
}
for _, path := range paths {
@ -429,7 +417,7 @@ func CreateWorkspaceDir(path string) {
// createDir creates a directory on the path if it not exists.
func createDir(path string) {
if !util.File.IsExist(path) {
if !gulu.File.IsExist(path) {
if err := os.MkdirAll(path, 0775); nil != err {
logger.Error(err)

View File

@ -1,18 +1,16 @@
{
"IP": "${ip}",
"Port": "7070",
"Context": "",
"Server": "{IP}:{Port}",
"StaticServer": "",
"Server": "http://127.0.0.1:7070",
"LogLevel": "debug",
"Channel": "ws://{IP}:{Port}",
"Data": "${home}/wide",
"RuntimeMode": "prod",
"HTTPSessionMaxAge": 86400,
"StaticResourceVersion": "${time}",
"MaxProcs": 4,
"RuntimeMode": "dev",
"WD": "${pwd}",
"Locale": "en_US",
"Playground": "${home}/playground",
"AllowRegister": true,
"Autocomplete": true
"Locale": "zh_CN",
"SiteStatCode": "",
"ReadOnly": false,
"OAuthLoginURL": "",
"OAuthAccessTokenURL": "",
"OAuthUserInfoURL": "",
"OAuthClientID": "",
"OAuthClientSecret": ""
}

View File

@ -1,24 +0,0 @@
#!/bin/bash
# see https://gist.github.com/hailiang/0f22736320abe6be71ce for more details
set -e
# Run test coverage on each subdirectories and merge the coverage profile.
echo "mode: count" > profile.cov
# Standard go tooling behavior is to ignore dirs with leading underscors
for dir in $(find . -maxdepth 10 -not -path './.git*' -not -path '*/_*' -type d);
do
if ls $dir/*.go &> /dev/null; then
go test -covermode=count -coverprofile=$dir/profile.tmp $dir
if [ -f $dir/profile.tmp ]
then
cat $dir/profile.tmp | tail -n +2 >> profile.cov
rm $dir/profile.tmp
fi
fi
done
go tool cover -func profile.cov

BIN
deps/github.com.tar.gz vendored

Binary file not shown.

BIN
deps/golang.org.tar.gz vendored

Binary file not shown.

View File

@ -1,2 +0,0 @@
* [User Guide](https://www.gitbook.com/book/88250/wide-user-guide)
* [Developer Guide](https://www.gitbook.com/book/88250/wide-dev-guide)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -25,129 +25,63 @@ import (
"runtime"
"strconv"
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/file"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/gorilla/websocket"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/file"
"github.com/88250/wide/session"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
// WSHandler handles request of creating editor channel.
// XXX: NOT used at present
func WSHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
sid := httpSession.Values["id"].(string)
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
editorChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
ret := map[string]interface{}{"output": "Editor initialized", "cmd": "init-editor"}
err := editorChan.WriteJSON(&ret)
if nil != err {
return
}
session.EditorWS[sid] = &editorChan
logger.Tracef("Open a new [Editor] with session [%s], %d", sid, len(session.EditorWS))
args := map[string]interface{}{}
for {
if err := session.EditorWS[sid].ReadJSON(&args); err != nil {
return
}
code := args["code"].(string)
line := int(args["cursorLine"].(float64))
ch := int(args["cursorCh"].(float64))
offset := getCursorOffset(code, line, ch)
logger.Tracef("offset: %d", offset)
gocode := util.Go.GetExecutableInGOBIN("gocode")
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
var output bytes.Buffer
cmd := exec.Command(gocode, argv...)
cmd.Stdout = &output
stdin, _ := cmd.StdinPipe()
cmd.Start()
stdin.Write([]byte(code))
stdin.Close()
cmd.Wait()
ret = map[string]interface{}{"output": string(output.Bytes()), "cmd": "autocomplete"}
if err := session.EditorWS[sid].WriteJSON(&ret); err != nil {
logger.Error("Editor WS ERROR: " + err.Error())
return
}
}
}
var logger = gulu.Log.NewLogger(os.Stdout)
// AutocompleteHandler handles request of code autocompletion.
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
var args map[string]interface{}
if conf.Wide.ReadOnly {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session, _ := session.HTTPSession.Get(r, "wide-session")
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
path := args["path"].(string)
fout, err := os.Create(path)
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
code := args["code"].(string)
fout.WriteString(code)
if err := fout.Close(); nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
line := int(args["cursorLine"].(float64))
ch := int(args["cursorCh"].(float64))
offset := getCursorOffset(code, line, ch)
logger.Tracef("offset: %d", offset)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
libPath := ""
for _, workspace := range workspaces {
@ -158,8 +92,8 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
logger.Tracef("gocode set lib-path [%s]", libPath)
gocode := gulu.Go.GetExecutableInGOBIN("gocode")
// FIXME: using gocode set lib-path has some issues while accrossing workspaces
gocode := util.Go.GetExecutableInGOBIN("gocode")
exec.Command(gocode, []string{"set", "lib-path", libPath}...).Run()
argv := []string{"-f=json", "--in=" + path, "autocomplete", strconv.Itoa(offset)}
@ -168,7 +102,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -179,16 +113,16 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
// GetExprInfoHandler handles request of getting expression infomation.
func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
username := session.Values["username"].(string)
session, _ := session.HTTPSession.Get(r, session.CookieName)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -201,7 +135,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -211,7 +145,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -223,24 +157,24 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
logger.Tracef("offset [%d]", offset)
ideStub := util.Go.GetExecutableInGOBIN("gotools")
ideStub := gulu.Go.GetExecutableInGOBIN("gotools")
argv := []string{"types", "-pos", filename + ":" + strconv.Itoa(offset), "-info", "."}
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
exprInfo := strings.TrimSpace(string(output))
if "" == exprInfo {
result.Succ = false
result.Code = -1
return
}
@ -250,21 +184,21 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
// FindDeclarationHandler handles request of finding declaration.
func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -277,7 +211,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -287,7 +221,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -299,24 +233,24 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
logger.Tracef("offset [%d]", offset)
ideStub := util.Go.GetExecutableInGOBIN("gotools")
ideStub := gulu.Go.GetExecutableInGOBIN("gotools")
argv := []string{"types", "-pos", filename + ":" + strconv.Itoa(offset), "-def", "."}
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
found := strings.TrimSpace(string(output))
if "" == found {
result.Succ = false
result.Code = -1
return
}
@ -338,22 +272,22 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
// FindUsagesHandler handles request of finding usages.
func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -366,7 +300,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -376,7 +310,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -387,24 +321,24 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
offset := getCursorOffset(code, line, ch)
logger.Tracef("offset [%d]", offset)
ideStub := util.Go.GetExecutableInGOBIN("gotools")
ideStub := gulu.Go.GetExecutableInGOBIN("gotools")
argv := []string{"types", "-pos", filename + ":" + strconv.Itoa(offset), "-use", "."}
cmd := exec.Command(ideStub, argv...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
out := strings.TrimSpace(string(output))
if "" == out {
result.Succ = false
result.Code = -1
return
}
@ -453,8 +387,8 @@ func getCursorOffset(code string, line, ch int) (offset int) {
return offset
}
func setCmdEnv(cmd *exec.Cmd, username string) {
userWorkspace := conf.GetUserWorkspace(username)
func setCmdEnv(cmd *exec.Cmd, userId string) {
userWorkspace := conf.GetUserWorkspace(userId)
cmd.Env = append(cmd.Env,
"GOPATH="+userWorkspace,

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -20,9 +20,9 @@ import (
"os"
"os/exec"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/session"
)
// GoFmtHandler handles request of formatting Go source code.
@ -31,30 +31,36 @@ import (
// 1. gofmt
// 2. goimports
func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := session.Values["username"].(string)
uid := session.Values["uid"].(string)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
filePath := args["file"].(string)
if util.Go.IsAPI(filePath) {
result.Succ = false
if gulu.Go.IsAPI(filePath) {
result.Code = -1
return
}
@ -63,7 +69,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -73,7 +79,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
fout.WriteString(code)
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -85,7 +91,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
result.Data = data
fmt := conf.GetGoFmt(username)
fmt := conf.GetGoFmt(uid)
argv := []string{filePath}
cmd := exec.Command(fmt, argv...)
@ -94,7 +100,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
output := string(bytes)
if "" == output {
// format error, returns the original content
result.Succ = true
result.Code = 0
return
}
@ -106,7 +112,7 @@ func GoFmtHandler(w http.ResponseWriter, r *http.Request) {
fout.WriteString(code)
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -18,8 +18,7 @@ package event
import (
"os"
"github.com/b3log/wide/log"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
)
const (
@ -39,7 +38,7 @@ const (
const maxQueueLength = 10
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Event represents an event.
type Event struct {
@ -70,7 +69,7 @@ var UserEventQueues = queues{}
// Load initializes the event handling.
func Load() {
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for event := range EventQueue {
logger.Debugf("Received a global event [code=%d]", event.Code)
@ -107,7 +106,7 @@ func (ueqs queues) New(sid string) *UserEventQueue {
ueqs[sid] = q
go func() { // start listening
defer util.Recover()
defer gulu.Panic.Recover(nil)
for evt := range q.Queue {
logger.Debugf("Session [%s] received an event [%d]", sid, evt.Code)

View File

@ -1,57 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"encoding/json"
"net/http"
"path/filepath"
"github.com/b3log/wide/util"
)
// DecompressHandler handles request of decompressing zip/tar.gz.
func DecompressHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
path := args["path"].(string)
// base := filepath.Base(path)
dir := filepath.Dir(path)
if !util.File.IsExist(path) {
result.Succ = false
result.Msg = "Can't find file [" + path + "] to descompress"
return
}
err := util.Zip.Unzip(path, dir)
if nil != err {
logger.Error(err)
result.Succ = false
return
}
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -20,7 +20,7 @@ import (
"os"
"path/filepath"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
)
// GetZipHandler handles request of retrieving zip file.
@ -34,7 +34,7 @@ func GetZipHandler(w http.ResponseWriter, r *http.Request) {
return
}
if !util.File.IsExist(path) {
if !gulu.File.IsExist(path) {
http.Error(w, "Not Found", 404)
return
@ -51,13 +51,13 @@ func GetZipHandler(w http.ResponseWriter, r *http.Request) {
// CreateZipHandler handles request of creating zip.
func CreateZipHandler(w http.ResponseWriter, r *http.Request) {
data := util.NewResult()
defer util.RetResult(w, r, data)
data := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, data)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
data.Succ = false
data.Code = -1
return
}
@ -75,24 +75,24 @@ func CreateZipHandler(w http.ResponseWriter, r *http.Request) {
dir := filepath.Dir(path)
if !util.File.IsExist(path) {
data.Succ = false
if !gulu.File.IsExist(path) {
data.Code = -1
data.Msg = "Can't find file [" + path + "]"
return
}
zipPath := filepath.Join(dir, name)
zipFile, err := util.Zip.Create(zipPath + ".zip")
zipFile, err := gulu.Zip.Create(zipPath + ".zip")
if nil != err {
logger.Error(err)
data.Succ = false
data.Code = -1
return
}
defer zipFile.Close()
if util.File.IsDir(path) {
if gulu.File.IsDir(path) {
zipFile.AddDirectory(base, path)
} else {
zipFile.AddEntry(base, path)

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -24,15 +24,14 @@ import (
"sort"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/event"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/event"
"github.com/88250/wide/session"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Node represents a file node in file tree.
type Node struct {
@ -61,7 +60,7 @@ var apiNode *Node
// initAPINode builds the Go API file node.
func initAPINode() {
apiPath := util.Go.GetAPIPath()
apiPath := gulu.Go.GetAPIPath()
apiNode = &Node{Name: "Go API", Path: apiPath, IconSkin: "ico-ztree-dir-api ", Type: "d",
Creatable: false, Removable: false, IsGoAPI: true, Children: []*Node{}}
@ -74,18 +73,18 @@ func initAPINode() {
// The Go API source code package also as a child node,
// so that users can easily view the Go API source code in file tree.
func GetFilesHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetGzResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetGzResult(w, r, result)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", IsParent: true, Children: []*Node{}}
@ -123,18 +122,18 @@ func GetFilesHandler(w http.ResponseWriter, r *http.Request) {
// RefreshDirectoryHandler handles request of refresh a directory of file tree.
func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
r.ParseForm()
path := r.FormValue("path")
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !gulu.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -156,37 +155,37 @@ func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) {
// GetFileHandler handles request of opening file by editor.
func GetFileHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
path := args["path"].(string)
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !gulu.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
size := util.File.GetFileSize(path)
size := gulu.File.GetFileSize(path)
if size > 5242880 { // 5M
result.Succ = false
result.Code = -1
result.Msg = "This file is too large to open :("
return
@ -199,30 +198,30 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
extension := filepath.Ext(path)
if util.File.IsImg(extension) {
if gulu.File.IsImg(extension) {
// image file will be open in a browser tab
data["mode"] = "img"
username := conf.GetOwner(path)
if "" == username {
logger.Warnf("The path [%s] has no owner")
userId := conf.GetOwner(path)
if "" == userId {
logger.Warnf("The path [%s] has no owner", path)
data["path"] = ""
return
}
user := conf.GetUser(username)
user := conf.GetUser(uid)
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.GetWorkspace(), "", 1)
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.WorkspacePath(), "", 1)
return
}
content := string(buf)
if util.File.IsBinary(content) {
result.Succ = false
if gulu.File.IsBinary(content) {
result.Code = -1
result.Msg = "Can't open a binary file :("
} else {
data["content"] = content
@ -232,22 +231,28 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
// SaveFileHandler handles request of saving file.
func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -255,7 +260,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
filePath := args["file"].(string)
sid := args["sid"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if gulu.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -265,7 +270,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -276,7 +281,7 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
wSession := session.WideSessions.Get(sid)
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
@ -288,29 +293,29 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
// NewFileHandler handles request of creating file or directory.
func NewFileHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
path := args["path"].(string)
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
if gulu.Go.IsAPI(path) || !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -322,7 +327,7 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
wSession := session.WideSessions.Get(sid)
if !createFile(path, fileType) {
result.Succ = false
result.Code = -1
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't create file " + path}
@ -331,38 +336,38 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
}
if "f" == fileType {
logger.Debugf("Created a file [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Created a file [%s] by user [%s]", path, wSession.UserId)
} else {
logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.UserId)
}
}
// RemoveFileHandler handles request of removing file or directory.
func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
path := args["path"].(string)
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
if gulu.Go.IsAPI(path) || !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -373,7 +378,7 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
wSession := session.WideSessions.Get(sid)
if !removeFile(path) {
result.Succ = false
result.Code = -1
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't remove file " + path}
@ -381,41 +386,41 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.Username)
logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.UserId)
}
// RenameFileHandler handles request of renaming file or directory.
func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
oldPath := args["oldPath"].(string)
if util.Go.IsAPI(oldPath) ||
!session.CanAccess(username, oldPath) {
if gulu.Go.IsAPI(oldPath) ||
!session.CanAccess(uid, oldPath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
newPath := args["newPath"].(string)
if util.Go.IsAPI(newPath) || !session.CanAccess(username, newPath) {
if gulu.Go.IsAPI(newPath) || !session.CanAccess(uid, newPath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -426,7 +431,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
wSession := session.WideSessions.Get(sid)
if !renameFile(oldPath, newPath) {
result.Succ = false
result.Code = -1
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't rename file " + oldPath}
@ -434,7 +439,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
return
}
logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.Username)
logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.UserId)
}
// Use to find results sorting.
@ -451,27 +456,27 @@ func (f foundPaths) Less(i, j int) bool { return f[i].score > f[j].score }
// FindHandler handles request of find files under the specified directory with the specified filename pattern.
func FindHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
path := args["path"].(string) // path of selected file in file tree
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
if !gulu.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -479,10 +484,10 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
name := args["name"].(string)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(uid)
workspaces := filepath.SplitList(userWorkspace)
if "" != path && !util.File.IsDir(path) {
if "" != path && !gulu.File.IsDir(path) {
path = filepath.Dir(path)
}
@ -492,7 +497,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
rs := find(workspace+conf.PathSeparator+"src", name, []*string{})
for _, r := range rs {
substr := util.Str.LCS(path, *r)
substr := gulu.Str.LCS(path, *r)
founds = append(founds, &foundPath{Path: filepath.ToSlash(*r), score: len(substr)})
}
@ -505,21 +510,21 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
// SearchTextHandler handles request of searching files under the specified directory with the specified keyword.
func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -527,7 +532,7 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
result.Code = -1
return
}
@ -536,7 +541,7 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
dir := args["dir"].(string)
if "" == dir {
userWorkspace := conf.GetUserWorkspace(wSession.Username)
userWorkspace := conf.GetUserWorkspace(wSession.UserId)
workspaces := filepath.SplitList(userWorkspace)
dir = workspaces[0]
}
@ -545,7 +550,7 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
text := args["text"].(string)
founds := []*Snippet{}
if util.File.IsDir(dir) {
if gulu.File.IsDir(dir) {
founds = search(dir, extension, text, []*Snippet{})
} else {
founds = searchInFile(dir, text)
@ -644,7 +649,7 @@ func listFiles(dirname string) []string {
//
// Refers to the zTree document for CSS class names.
func getIconSkin(filenameExtension string) string {
if util.File.IsImg(filenameExtension) {
if gulu.File.IsImg(filenameExtension) {
return "ico-ztree-img "
}
@ -763,7 +768,7 @@ func find(dir, name string, results []*string) []*string {
path := dir + fname
if fileInfo.IsDir() {
if util.Str.Contains(fname, defaultExcludesFind) {
if gulu.Str.Contains(fname, defaultExcludesFind) {
continue
}
@ -836,7 +841,7 @@ func searchInFile(path string, text string) []*Snippet {
}
content := string(bytes)
if util.File.IsBinary(content) {
if gulu.File.IsBinary(content) {
return ret
}

View File

@ -1,77 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package file
import (
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"github.com/b3log/wide/util"
)
type fileInfo struct {
Name string `json:"name"`
Type string `json:"type"`
Error string `json:"error,omitempty"`
}
func handleUpload(p *multipart.Part, dir string) (fi *fileInfo) {
fi = &fileInfo{
Name: p.FileName(),
Type: p.Header.Get("Content-Type"),
}
path := filepath.Clean(dir + "/" + fi.Name)
f, _ := os.Create(path)
io.Copy(f, p)
f.Close()
return
}
func handleUploads(r *http.Request, dir string) (fileInfos []*fileInfo) {
fileInfos = make([]*fileInfo, 0)
mr, err := r.MultipartReader()
part, err := mr.NextPart()
for err == nil {
if name := part.FormName(); name != "" {
if part.FileName() != "" {
fileInfos = append(fileInfos, handleUpload(part, dir))
}
}
part, err = mr.NextPart()
}
return
}
// UploadHandler handles request of file upload.
func UploadHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
q := r.URL.Query()
dir := q["path"][0]
result.Data = handleUploads(r, dir)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -23,7 +23,7 @@ import (
"net/http"
"strings"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
)
type element struct {
@ -34,14 +34,14 @@ type element struct {
// GetOutlineHandler gets outfile of a go file.
func GetOutlineHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -51,7 +51,7 @@ func GetOutlineHandler(w http.ResponseWriter, r *http.Request) {
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, "", code, 0)
if err != nil {
result.Succ = false
result.Code = -1
return
}

23
go.mod Normal file
View File

@ -0,0 +1,23 @@
module github.com/88250/wide
go 1.20
require (
github.com/88250/gulu v1.1.0
github.com/fsnotify/fsnotify v1.4.9
github.com/gorilla/sessions v1.2.0
github.com/gorilla/websocket v1.4.2
github.com/parnurzeal/gorequest v0.2.16
)
require (
github.com/davidebianchi/go-jsonclient v1.5.0 // indirect
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/smartystreets/goconvey v1.6.4 // indirect
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b // indirect
golang.org/x/text v0.3.2 // indirect
moul.io/http2curl v1.0.0 // indirect
)

44
go.sum Normal file
View File

@ -0,0 +1,44 @@
github.com/88250/gulu v1.1.0 h1:aU8HncW1XNssT1insCOz6rvmTUDrtsDwSnzeqTEqNFA=
github.com/88250/gulu v1.1.0/go.mod h1:a2POIziN+QFeNT4Mj7FHH2lz1HEaFlMRF6wPE0NHM4U=
github.com/davidebianchi/go-jsonclient v1.5.0 h1:PVDunAF/6c30D2SSx711efsrUP3hhml+uGT6oD3lzVY=
github.com/davidebianchi/go-jsonclient v1.5.0/go.mod h1:lXd/hkx23H590Dod74j5GsmpgxF8RIAGZDt1P5+2mqo=
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1 h1:TEmChtx8+IeOghiySC8kQIr0JZOdKUmRmmkuRDuYs3E=
github.com/elazarl/goproxy v0.0.0-20200426045556-49ad98f6dac1/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/parnurzeal/gorequest v0.2.16 h1:T/5x+/4BT+nj+3eSknXmCTnEVGSzFzPGdpqmUVVZXHQ=
github.com/parnurzeal/gorequest v0.2.16/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/rogpeppe/go-charset v0.0.0-20180617210344-2471d30d28b4/go.mod h1:qgYeAmZ5ZIpBWTGllZSQnw97Dj+woV0toclVaRGI8pc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b h1:h03Ur1RlPrGTjua4koYdpGl8W0eYo8p1uI9w7RPlkdk=
golang.org/x/sys v0.0.0-20200408040146-ea54a3c99b9b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2015, b3log.org
* Copyright (c) 2014-2018, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -18,49 +18,57 @@
* @file frontend tool.
*
* @author <a href="mailto:liliyuan@fangstar.net">Liyuan Li</a>
* @version 0.1.0.0, Dec 15, 2015
* @version 0.2.0.0, Oct 5, 2018
*/
var gulp = require("gulp");
var concat = require('gulp-concat');
var minifyCSS = require('gulp-minify-css');
var uglify = require('gulp-uglify');
var sourcemaps = require("gulp-sourcemaps");
var gulp = require('gulp')
var concat = require('gulp-concat')
var cleanCSS = require('gulp-clean-css')
var uglify = require('gulp-uglify')
var sourcemaps = require('gulp-sourcemaps')
gulp.task('cc', function () {
function minLibCSS () {
// css
var cssLibs = ['./static/js/lib/jquery-layout/layout-default-latest.css',
var cssLibs = [
'./static/js/lib/jquery-layout/layout-default-latest.css',
'./static/js/lib/codemirror-5.1/codemirror.css',
'./static/js/lib/codemirror-5.1/addon/hint/show-hint.css',
'./static/js/lib/codemirror-5.1/addon/lint/lint.css',
'./static/js/lib/codemirror-5.1/addon/fold/foldgutter.css',
'./static/js/lib/codemirror-5.1/addon/dialog/dialog.css',
'./static/js/overwrite/codemirror/theme/*.css'];
gulp.src(cssLibs)
.pipe(minifyCSS())
.pipe(concat('lib.min.css'))
.pipe(gulp.dest('./static/css/'));
'./static/js/overwrite/codemirror/theme/*.css']
return gulp.src(cssLibs).
pipe(cleanCSS()).
pipe(concat('lib.min.css')).
pipe(gulp.dest('./static/css/'))
}
gulp.src('./static/js/lib/ztree/zTreeStyle.css')
.pipe(minifyCSS())
.pipe(concat('zTreeStyle.min.css'))
.pipe(gulp.dest('./static/js/lib/ztree/'));
function minZTreeStyleCSS () {
return gulp.src('./static/js/lib/ztree/zTreeStyle.css').
pipe(cleanCSS()).
pipe(concat('zTreeStyle.min.css')).
pipe(gulp.dest('./static/js/lib/ztree/'))
}
var cssWide = ['./static/css/dialog.css',
function minWideCSS () {
var cssWide = [
'./static/css/dialog.css',
'./static/css/base.css',
'./static/css/wide.css',
'./static/css/side.css',
'./static/css/start.css',
'./static/css/about.css'
];
gulp.src(cssWide)
.pipe(minifyCSS())
.pipe(concat('wide.min.css'))
.pipe(gulp.dest('./static/css/'));
'./static/css/about.css',
]
return gulp.src(cssWide).
pipe(cleanCSS()).
pipe(concat('wide.min.css')).
pipe(gulp.dest('./static/css/'))
}
function minLibJS () {
// js
var jsLibs = ['./static/js/lib/jquery-2.1.1.min.js',
var jsLibs = [
'./static/js/lib/jquery-2.1.1.min.js',
'./static/js/lib/jquery-ui.min.js',
'./static/js/lib/jquery-layout/jquery.layout-latest.js',
'./static/js/lib/reconnecting-websocket.js',
@ -93,8 +101,8 @@ gulp.task('cc', function () {
'./static/js/lib/codemirror-5.1/addon/fold/xml-fold.js',
'./static/js/lib/codemirror-5.1/addon/fold/markdown-fold.js',
'./static/js/lib/codemirror-5.1/addon/fold/comment-fold.js',
'./static/js/lib/codemirror-5.1/addon/fold/mode/loadmode.js',
'./static/js/lib/codemirror-5.1/addon/fold/comment/comment.js',
'./static/js/lib/codemirror-5.1/addon/mode/loadmode.js',
'./static/js/lib/codemirror-5.1/addon/comment/comment.js',
'./static/js/lib/codemirror-5.1/mode/meta.js',
'./static/js/lib/codemirror-5.1/mode/go/go.js',
'./static/js/lib/codemirror-5.1/mode/clike/clike.js',
@ -107,13 +115,16 @@ gulp.task('cc', function () {
'./static/js/lib/codemirror-5.1/mode/sql/sql.js',
'./static/js/lib/codemirror-5.1/keymap/vim.js',
'./static/js/lib/lint/json-lint.js',
'./static/js/lib/lint/go-lint.js'];
gulp.src(jsLibs)
.pipe(uglify())
.pipe(concat('lib.min.js'))
.pipe(gulp.dest('./static/js/'));
'./static/js/lib/lint/go-lint.js']
return gulp.src(jsLibs).
pipe(uglify()).
pipe(concat('lib.min.js')).
pipe(gulp.dest('./static/js/'))
}
var jsWide = ['./static/js/tabs.js',
function minWideJS () {
var jsWide = [
'./static/js/tabs.js',
'./static/js/tabs.js',
'./static/js/dialog.js',
'./static/js/editors.js',
@ -124,12 +135,16 @@ gulp.task('cc', function () {
'./static/js/menu.js',
'./static/js/windows.js',
'./static/js/hotkeys.js',
'./static/js/bottomGroup.js'
];
gulp.src(jsWide)
.pipe(sourcemaps.init())
.pipe(uglify())
.pipe(concat('wide.min.js'))
.pipe(sourcemaps.write("."))
.pipe(gulp.dest('./static/js/'));
});
'./static/js/bottomGroup.js',
]
return gulp.src(jsWide).
pipe(sourcemaps.init()).
pipe(uglify()).
pipe(concat('wide.min.js')).
pipe(sourcemaps.write('.')).
pipe(gulp.dest('./static/js/'))
}
gulp.task('default',
gulp.series(
gulp.parallel(minLibCSS, minZTreeStyleCSS, minWideCSS, minLibJS, minWideJS)))

View File

@ -1,7 +1,7 @@
{
"colon": ": ",
"wide": "Wide",
"wide_title": "Team development, anytime, anywhere",
"wide_title": "Playing golang, anytime, anywhere",
"cancel": "Cancel",
"file": "File",
"login": "Login",
@ -28,7 +28,6 @@
"close_all_files": "Close All",
"save_all_files": "Save All",
"format": "Format",
"goget": "go get",
"goinstall": "go install",
"build": "Build",
"build_n_run": "Build&Run",
@ -102,11 +101,6 @@
"start-install": "START [go install]",
"install-succ": "[go install] SUCCESS",
"install-error": "[go install] ERROR",
"start-get": "START [go get]",
"get-succ": "[go get] SUCCESS",
"get-error": "[go get] ERROR",
"start-git_clone": "START [git clone]",
"git_clone-done": "[git clone] DONE",
"check_version": "Checking update",
"new_version_available": "new version available",
"go_env": "Go",
@ -120,7 +114,6 @@
"team": "Team",
"sing_up_error": "Sign Up Error",
"user_name_ruler": "Username only by az, AZ, 0-9, _ consisting of a length of 16",
"invalid_email": "Invalid Email",
"password_no_match": "Password doesn't match the confirmation",
"discard": "Discard",
"close": "Close",
@ -139,7 +132,6 @@
"clearOutput": "Clear Output",
"export": "Export",
"refresh": "Refresh",
"import": "Import",
"theme": "Theme",
"tab_size": "Tab Size",
"copy_file_path": "Copy File Path",
@ -158,11 +150,8 @@
"source": "Source",
"toggle_comment": "Toggle Comment",
"find_in_files": "Find in Files",
"email": "Email",
"no_empty": "Can not Empty!",
"change_avatar": "Avatar modify go",
"open": "Open",
"pricing": "Pricing",
"search_no_match": "No matching files were found.",
"outline": "Outline",
"govet": "go vet",
@ -172,12 +161,11 @@
"restore_outline": "Restore Outline",
"share": "Share",
"url": "URL",
"short_url": "Short URL",
"embeded": "Embeded",
"git_clone": "Git Clone",
"terms": "Terms",
"download": "Download",
"decompress": "Decompress",
"keymap": "Keymap",
"resize": "Resize"
"resize": "Resize",
"sponsor": "Sponsor"
}

View File

@ -1,7 +1,7 @@
{
"colon": "",
"wide": "Wide",
"wide_title": "チーム開発、いつでも、どこでも",
"wide_title": "いつでも、どこでもゴランをプレイする",
"cancel": "取消",
"file": "ファイル",
"login": "ログイン",
@ -28,7 +28,6 @@
"close_all_files": "全てのファイルを閉じる",
"save_all_files": "全てを保存",
"format": "フォーマット",
"goget": "go get",
"goinstall": "go install",
"build": "ビルド",
"build_n_run": "ビルド実行",
@ -102,11 +101,6 @@
"start-install": "[go install] 開始",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失敗",
"start-get": "[go get] 開始",
"get-succ": "[go get] 成功",
"get-error": "[go get] 失敗",
"start-git_clone": "[git clone] 開始",
"git_clone-done": "[git clone] 終わった",
"check_version": "更新をチェック中",
"new_version_available": "新しいバージョンがあります",
"go_env": "Go",
@ -120,18 +114,17 @@
"team": "チーム",
"sing_up_error": "登録に失敗しました",
"user_name_ruler": "16の長さからなる_ AZ、AZ、0-9、によってユーザ名のみ",
"invalid_email": "無効な電子メール",
"password_no_match": "一貫性のないパスワード入力",
"discard": "あきらめる",
"close": "クローズ",
"close_other": "閉じるその他",
"clear": "空の",
"preference": "偏好设定",
"preference": "環境設定",
"appearence": "エクステリア",
"gotool": "Go ツール",
"user": "ユーザー",
"font": "フォント",
"font_size": "フォント·サイズ",
"font_size": "フォントサイズ",
"line_height": "行の高さ",
"go_format": "Go フォーマット",
"locale": "ロケール",
@ -139,9 +132,8 @@
"clearOutput": "空の出力",
"export": "輸出",
"refresh": "リフレッシュ",
"import": "インポート",
"theme": "テーマ",
"tab_size": "Tab·サイズ",
"tab_size": "Tab サイズ",
"copy_file_path": "ファイルパスをコピー",
"file_tree": "ファイルツリー",
"select": "選択する",
@ -150,7 +142,7 @@
"edit": "編集",
"undo": "元に戻す",
"redo": "やり直し",
"cut": "Cut",
"cut": "切り取り",
"copy": "コピー",
"paste": "貼り付け",
"select_all": "すべて選択",
@ -158,26 +150,22 @@
"source": "ソース",
"toggle_comment": "トグルコメント",
"find_in_files": "ファイルから検索",
"email": "Eメール",
"no_empty": "空ではありません",
"change_avatar": "アバターの変更は行く",
"open": "オープン",
"pricing": "价格",
"search_no_match": "一致するファイルが見つかりませんでした。",
"outline": "アウトライン",
"govet": "go vet",
"start-vet": "START [go vet]",
"vet-succ": "[go vet] SUCCESS",
"vet-error": "[go vet] ERROR",
"start-vet": "[go vet] 開始",
"vet-succ": "[go vet] 成功",
"vet-error": "[go vet] 失敗",
"restore_outline": "アウトラインを復元",
"share": "シェア",
"url": "リンク",
"short_url": "ショートリンク",
"embeded": "埋め込む",
"git_clone": "Git クローン",
"terms": "利用規約",
"download": "ダウンロード",
"decompress": "解凍する",
"keymap": "キーマップ",
"resize": "サイズ変更"
"resize": "サイズ変更",
"sponsor": "スポンサー"
}

171
i18n/ko_KR.json Normal file
View File

@ -0,0 +1,171 @@
{
"colon": "",
"wide": "Wide",
"wide_title": "언제 어디서나 골란 놀기",
"cancel": "취소",
"file": "문서",
"login": "로그인",
"username": "아이디",
"current_user": "현제 접속자 ",
"current_session": "현제 세션",
"password": "비밀번호",
"login_error": "로그인 실패",
"run": "실행",
"debug": "디버깅",
"help": "도움말",
"check_update": "업데이트 확인",
"issues": "문제",
"wide_doc": "Wide 문서",
"about": "about",
"start_page": "시작 페이지",
"create_file": "새문서",
"create": "새로만들기",
"create_dir": "새폴더",
"delete": "삭제",
"rename": "이름변경",
"save": "저장",
"exit": "끝내기",
"close_all_files": "모두 닫기",
"save_all_files": "일괄저장",
"format": "포멧",
"goinstall": "go install",
"build": "빌드",
"build_n_run": "빌드후실행",
"editor": "편집기",
"max_editor": "최대화",
"restore_editor": "복구",
"unread_notification": "읽지않은 공지",
"notification_2": "[gocode] 를 찾지 못하였습니다. 자동완성기능이 동작하지 않습니다. ",
"notification_3": "[ide_stub] 를 찾지 못하였습니다. 찾기 기능이 동작하지 않습니다. ",
"notification_4": "서버 오류",
"goto_line": "라인이동",
"goto_file": "문서오픈",
"go": "이동",
"tip": "팁",
"confirm": "확인",
"stop": "정지",
"output": "출력",
"search": "검색",
"notification": "알림",
"min": "최소화",
"restore_side": "좌측창 복구",
"search_text": "문서검색",
"find": "검색",
"find_next": "다음검색",
"find_previous": "이전검색",
"replace": "바꾸기",
"replace_all": "전부바꾸기",
"restore_bottom": "아래창 복구",
"file_format": "확장자",
"keyword": "키워드",
"user_guide": "이용가이드",
"dev_guide": "개발 가이드",
"keyboard_shortcuts": "단축키",
"ver": "버전",
"current_ver": "현재버전",
"dev_team": "개발단체",
"donate": "기부",
"confirm_save": "정장했는지 확인해 주세요. ",
"workspace": "작업창",
"project_address": "항목주소",
"community": "커뮤니티",
"autocomplete": "자동완성",
"jump_to_decl": "알림으로 이동",
"show_expr_info": "Expression 보기",
"find_usages": "사용법찾기",
"delete_line": "현재 줄 삭제",
"copy_lines_up": "위로 복사",
"copy_lines_down": "아래로 복사",
"move_lines_up": "위로이동",
"move_lines_down": "아래로 이동",
"save_editor_file": "현재 편집하고있던 문서 저장",
"save_all_editors_files": "일괄저장",
"close_editor": "편집창 닫기",
"full_screen": "편집기 전체화면",
"auto_indent": "자동 정렬",
"indent": "안쪽이동",
"unindent": "밖으로 이동",
"focus": "포커스",
"switch_tab": "편집기편집/창편집 tab",
"focus_editor": "편집창으로 포커스",
"focus_file_tree": "프로젝트트리로 포커스이동",
"focus_output": "output 으로 포커스 이동",
"focus_search": "검색창으로 포커스 이동",
"focus_notification": "알림창으로 포커스 이동",
"start-build": "시작 [go build]",
"build-succ": "[go build] 성공",
"build-error": "[go build] 실패",
"start-test": "시작 [go test]",
"test-succ": "[go test] 성공",
"test-error": "[go test] 실패",
"start-install": "시작 [go install]",
"install-succ": "[go install] 성공",
"install-error": "[go install] 실패",
"check_version": "최신버전검색중",
"new_version_available": "최신업데이트 사용 가능",
"go_env": "Go 환경",
"os": "운영체제",
"project": "항목",
"license": "라이센스",
"credits": "감사합니다.",
"uptodate": "최신버전입니다.",
"test": "테스트",
"sign_up": "회원가입",
"team": "단체",
"sing_up_error": "가입실패",
"user_name_ruler": "아이디는 16글자 이하이며 a-z, A-Z, 0-9, _ 만 가능합니다,",
"password_no_match": "비밀번호 오류",
"discard": "취소",
"close": "닫기",
"close_other": "현재창 남기고 닫기",
"clear": "청소",
"preference": "설정",
"appearence": "외관",
"gotool": "Go 도구",
"user": "유저",
"font": "폰트",
"font_size": "폰트크기",
"line_height": "줄간격",
"go_format": "Go 포멧",
"locale": "언어설정",
"apply": "적용",
"clearOutput": "ouput 클리어",
"export": "내보내기",
"refresh": "새로고침",
"theme": "주제",
"tab_size": "Tab 크기",
"copy_file_path": "경로복사",
"file_tree": "트리",
"select": "선택",
"expand": "확장",
"collapse": "축소",
"edit": "편집",
"undo": "취소",
"redo": "복원",
"cut": "잘라내기",
"copy": "복사",
"paste": "붙여넣기",
"select_all": "전체선택",
"select_identifier": "표식선택",
"source": "소스",
"toggle_comment": "주석",
"find_in_files": "문서에서 찾기",
"no_empty": "값을 입력해 주세요.",
"open": "열기",
"search_no_match": "해당 문서를 찾지 못하였습니다.",
"outline": "주제",
"govet": "go vet",
"start-vet": "시작 [go vet]",
"vet-succ": "[go vet] 성공",
"vet-error": "[go vet] 실패",
"restore_outline": "주제복구",
"share": "공유",
"url": "하이퍼링크",
"embeded": "삽입",
"terms": "사용계약",
"download": "다운로드",
"decompress": "압축풀기",
"keymap": "단축키",
"resize": "크기조절",
"sponsor": "후원사"
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -17,16 +17,15 @@ package i18n
import (
"encoding/json"
"io/ioutil"
"os"
"sort"
"strings"
"github.com/b3log/wide/log"
"github.com/88250/gulu"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Locale.
type locale struct {
@ -44,6 +43,10 @@ func Load() {
names, _ := f.Readdirnames(-1)
f.Close()
if len(Locales) == len(names)-1 {
return
}
for _, name := range names {
if !strings.HasSuffix(name, ".json") {
continue
@ -55,7 +58,7 @@ func Load() {
}
func load(localeStr string) {
bytes, err := ioutil.ReadFile("i18n/" + localeStr + ".json")
bytes, err := os.ReadFile("i18n/" + localeStr + ".json")
if nil != err {
logger.Error(err)

View File

@ -1,7 +1,7 @@
{
"colon": "",
"wide": "Wide",
"wide_title": "团队开发,随时随地",
"wide_title": "随时随地玩 golang",
"cancel": "取消",
"file": "文件",
"login": "登录",
@ -28,7 +28,6 @@
"close_all_files": "关闭所有文件",
"save_all_files": "保存所有文件",
"format": "格式化",
"goget": "go get",
"goinstall": "go install",
"build": "构建",
"build_n_run": "构建并运行",
@ -102,11 +101,6 @@
"start-install": "开始 [go install]",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失败",
"start-get": "开始 [go get]",
"get-succ": "[go get] 成功",
"get-error": "[go get] 失败",
"start-git_clone": "开始 [git clone]",
"git_clone-done": "[git clone] 完成",
"check_version": "正在检查更新",
"new_version_available": "新版本可用",
"go_env": "Go 环境",
@ -120,7 +114,6 @@
"team": "团队",
"sing_up_error": "注册失败",
"user_name_ruler": "用户名只能由 a-z, A-Z, 0-9, _ 组成长度为16",
"invalid_email": "无效的电子邮件",
"password_no_match": "密码输入不一致",
"discard": "放弃",
"close": "关闭",
@ -139,7 +132,6 @@
"clearOutput": "清空输出",
"export": "导出",
"refresh": "刷新",
"import": "导入",
"theme": "主题",
"tab_size": "Tab 大小",
"copy_file_path": "复制文件路径",
@ -158,26 +150,22 @@
"source": "源码",
"toggle_comment": "注释",
"find_in_files": "在文件中查找",
"email": "电子邮件",
"no_empty": "不能为空",
"change_avatar": "头像修改请到",
"open": "打开",
"pricing": "价格",
"search_no_match": "没有发现匹配的文件。",
"outline": "大纲",
"govet": "go vet",
"start-vet": "START [go vet]",
"vet-succ": "[go vet] SUCCESS",
"vet-error": "[go vet] ERROR",
"start-vet": "开始 [go vet]",
"vet-succ": "[go vet] 成功",
"vet-error": "[go vet] 失败",
"restore_outline": "恢复大纲",
"share": "分享",
"url": "链接",
"short_url": "短链接",
"embeded": "嵌入",
"git_clone": "Git 克隆",
"terms": "使用条款",
"download": "下载",
"decompress": "解压缩",
"keymap": "快捷键",
"resize": "调整大小"
"resize": "调整大小",
"sponsor": "赞助"
}

View File

@ -1,112 +1,106 @@
{
"colon": "",
"wide": "Wide",
"wide_title": "團隊開發,隨時隨地",
"wide_title": "隨時隨地玩 golang",
"cancel": "取消",
"file": "文件檔",
"login": "登",
"username": "用户名字",
"current_user": "當前用户名",
"file": "",
"login": "登",
"username": "使用者",
"current_user": "當前使用者",
"current_session": "當前會話",
"password": "密碼",
"login_error": "登錄失败",
"login_error": "登入失敗",
"run": "執行",
"debug": "除蟲",
"debug": "Debug",
"help": "說明書",
"check_update": "程式更新?",
"check_update": "檢查更新?",
"issues": "問題",
"wide_doc": "Wide 說明文件",
"wide_doc": "Wide 說明",
"about": "關於",
"start_page": "起始頁",
"create_file": "建立文件",
"create": "",
"create_dir": "建立目錄",
"start_page": "開始頁面",
"create_file": "開新檔案",
"create": "建",
"create_dir": "新增資料夾",
"delete": "删除",
"rename": "重命名",
"rename": "重命名",
"save": "儲存",
"exit": "退出",
"close_all_files": "關閉所有文件",
"save_all_files": "保存所有文件",
"exit": "離開",
"close_all_files": "關閉所有檔案",
"save_all_files": "儲存所有檔案",
"format": "格式化",
"goget": "go get",
"goinstall": "go install",
"build": "編譯軟件",
"build_n_run": "編譯軟件並執行",
"build": "編譯",
"build_n_run": "編譯並執行",
"editor": "編輯器",
"max_editor": "編輯器窗口最大化",
"restore_editor": "編輯器窗口還原",
"max_editor": "編輯器最大化",
"restore_editor": "編輯器還原",
"unread_notification": "未讀通知",
"notification_2": "没有檢查到 gocode這將會導致 [自動完成] 失效",
"notification_3": "没有檢查到 ide_stub這將會導致 [跳轉到聲明]、[查找使用] 失效",
"notification_4": "器內部錯誤",
"notification_2": "没有檢查到 gocode這將會導致「自動完成」失效",
"notification_3": "没有檢查到 ide_stub這將會導致「跳轉到聲明」、「查找使用」失效",
"notification_4": "服器內部錯誤",
"goto_line": "跳轉到行",
"goto_file": "打開文件",
"goto_file": "開啟舊檔",
"go": "跳到",
"tip": "提示",
"confirm": "確定",
"stop": "停止",
"output": "出",
"search": "搜",
"output": "出",
"search": "搜",
"notification": "通知",
"min": "最小",
"restore_side": "左側還原",
"search_text": "尋找文本",
"min": "縮到最小",
"restore_side": "左側窗還原",
"search_text": "尋找",
"find": "尋找",
"find_next": "尋找下一個",
"find_previous": "尋找上一個",
"replace": "替換",
"replace_all": "替換全部",
"restore_bottom": "底部還原",
"replace": "取代",
"replace_all": "取代全部",
"restore_bottom": "底部窗還原",
"file_format": "文件格式",
"keyword": "關鍵字",
"user_guide": "用户指南",
"dev_guide": "開發指南",
"user_guide": "使用者說明文件",
"dev_guide": "開發說明文件",
"keyboard_shortcuts": "鍵盤快捷鍵",
"ver": "版本",
"current_ver": "當前版本",
"dev_team": "開發團隊",
"donate_us": "愛心捐",
"confirm_save": "請確認所有文件已保存",
"donate_us": "愛心捐",
"confirm_save": "請確認所有檔案都已儲存",
"workspace": "工作空間",
"project_address": "項目地址",
"community": "社區",
"autocomplete": "自動補全",
"autocomplete": "自動完成",
"jump_to_decl": "跳轉到聲明",
"show_expr_info": "查看表達式信息",
"find_usages": "尋找使用",
"delete_line": "删除當前行",
"copy_lines_up": "複製到上",
"copy_lines_down": "複製到下",
"move_lines_up": "移動到上",
"move_lines_down": "移動到下",
"save_editor_file": "保存當前編輯器文件",
"save_all_editors_files": "保存所有編輯器文件",
"copy_lines_up": "複製到上一行",
"copy_lines_down": "複製到下一行",
"move_lines_up": "移動到上一行",
"move_lines_down": "移動到下一行",
"save_editor_file": "儲存當前編輯檔案",
"save_all_editors_files": "儲存所有檔案",
"close_editor": "關閉當前編輯器",
"full_screen": "編輯器全螢幕",
"full_screen": "全螢幕",
"auto_indent": "自動縮進",
"indent": "縮進",
"unindent": "縮進還原",
"focus": "焦點",
"switch_tab": "切換編輯器/窗口组 tab",
"focus_editor": "焦點切換到編輯器",
"focus_file_tree": "焦點切換到文件樹",
"focus_output": "焦點切換到输出窗口",
"focus_search": "焦點切換到搜索窗口",
"focus_notification": "焦點切換到通知窗口",
"switch_tab": "切換編輯器/視窗组 tab",
"focus_editor": "切換至編輯器",
"focus_file_tree": "切換至檔案樹",
"focus_output": "切換至输出視窗",
"focus_search": "切換至搜索視窗",
"focus_notification": "切換至通知視窗",
"start-build": "開始 [go build]",
"build-succ": "[go build] 成功",
"build-error": "[go build] 失",
"build-error": "[go build] 失",
"start-test": "開始 [go test]",
"test-succ": "[go test] 成功",
"test-error": "[go test] 失",
"test-error": "[go test] 失",
"start-install": "開始 [go install]",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失败",
"start-get": "開始 [go get]",
"get-succ": "[go get] 成功",
"get-error": "[go get] 失败",
"start-git_clone": "開始 [git clone]",
"git_clone-done": "[git clone] 完成",
"install-error": "[go install] 失敗",
"check_version": "正在檢查更新",
"new_version_available": "可用新版本",
"go_env": "Go 環境",
@ -119,18 +113,17 @@
"sign_up": "註冊",
"team": "團隊",
"sing_up_error": "註冊失敗",
"user_name_ruler": "用戶名只能由az, AZ, 0-9, _ 組成長度為16",
"invalid_email": "無效的電子郵件",
"user_name_ruler": "帳號只能由 az, AZ, 0-9, _ 組成長度為16",
"password_no_match": "密碼輸入不一致",
"discard": "棄",
"discard": "棄",
"close": "關閉",
"close_other": "關閉其它",
"clear": "清空",
"preference": "偏好設定",
"appearence": "外觀",
"gotool": "Go 工具",
"user": "用戶",
"font": "字",
"user": "使用者",
"font": "字",
"font_size": "字體大小",
"line_height": "行高",
"go_format": "Go 格式化",
@ -139,45 +132,40 @@
"clearOutput": "清空輸出",
"export": "導出",
"refresh": "刷新",
"import": "導入",
"theme": "主題",
"tab_size": "Tab 大小",
"copy_file_path": "複製文件路徑",
"copy_file_path": "複製檔案位置",
"file_tree": "文件樹",
"select": "選擇",
"expand": "展開",
"collapse": "收起",
"edit": "編輯",
"undo": "撤消",
"redo": "重做",
"cut": "剪",
"undo": "復原",
"redo": "回復",
"cut": "剪",
"copy": "複製",
"paste": "粘貼",
"paste": "天上",
"select_all": "全選",
"select_identifier": "選擇標識符",
"source": "源代碼",
"toggle_comment": "註釋",
"find_in_files": "在文件中查找",
"email": "電子郵件",
"source": "原始碼",
"toggle_comment": "註解",
"find_in_files": "在文件中尋找",
"no_empty": "不能為空",
"change_avatar": "頭像修改請到",
"open": "打開",
"pricing": "價格",
"open": "開啟",
"search_no_match": "沒有發現匹配的文件。",
"outline": "大綱",
"govet": "go vet",
"start-vet": "START [go vet]",
"vet-succ": "[go vet] SUCCESS",
"vet-error": "[go vet] ERROR",
"start-vet": "開始 [go vet]",
"vet-succ": "[go vet] 成功",
"vet-error": "[go vet] 失敗",
"restore_outline": "恢復大綱",
"share": "分享",
"url": "鏈接",
"short_url": "短鏈接",
"url": "連結",
"embeded": "嵌入",
"git_clone": "Git 克隆",
"terms": "使用條款",
"download": "下載",
"decompress": "解壓縮",
"keymap": "快捷鍵",
"resize": "調整大小"
"keymap": "快速鍵",
"resize": "調整大小",
"sponsor": "贊助"
}

View File

@ -1,217 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package log includes logging related manipulations.
//
// log.SetLevel("debug")
// logger := log.NewLogger(os.Stdout)
//
// logger.Trace("trace message)
// logger.Debug("debug message")
// logger.Info("info message")
// logger.Warn("warning message")
// logger.Error("error message")
//
// logger.Errorf("formatted %s message", "error")
package log
import (
"fmt"
"io"
stdlog "log"
"strings"
)
// Logging level.
const (
Off = iota
Trace
Debug
Info
Warn
Error
)
// all loggers.
var loggers []*Logger
// the global default logging level, it will be used for creating logger.
var logLevel = Debug
// Logger represents a simple logger with level.
// The underlying logger is the standard Go logging "log".
type Logger struct {
level int
logger *stdlog.Logger
}
// NewLogger creates a logger.
func NewLogger(out io.Writer) *Logger {
ret := &Logger{level: logLevel, logger: stdlog.New(out, "", stdlog.Ldate|stdlog.Ltime|stdlog.Lshortfile)}
loggers = append(loggers, ret)
return ret
}
// SetLevel sets the logging level of all loggers.
func SetLevel(level string) {
logLevel = getLevel(level)
for _, l := range loggers {
l.SetLevel(level)
}
}
// getLevel gets logging level int value corresponding to the specified level.
func getLevel(level string) int {
level = strings.ToLower(level)
switch level {
case "off":
return Off
case "trace":
return Trace
case "debug":
return Debug
case "info":
return Info
case "warn":
return Warn
case "error":
return Error
default:
return Info
}
}
// SetLevel sets the logging level of a logger.
func (l *Logger) SetLevel(level string) {
l.level = getLevel(level)
}
// IsTraceEnabled determines whether the trace level is enabled.
func (l *Logger) IsTraceEnabled() bool {
return l.level <= Trace
}
// IsDebugEnabled determines whether the debug level is enabled.
func (l *Logger) IsDebugEnabled() bool {
return l.level <= Debug
}
// IsWarnEnabled determines whether the debug level is enabled.
func (l *Logger) IsWarnEnabled() bool {
return l.level <= Warn
}
// Trace prints trace level message.
func (l *Logger) Trace(v ...interface{}) {
if Trace < l.level {
return
}
l.logger.SetPrefix("T ")
l.logger.Output(2, fmt.Sprint(v...))
}
// Tracef prints trace level message with format.
func (l *Logger) Tracef(format string, v ...interface{}) {
if Trace < l.level {
return
}
l.logger.SetPrefix("T ")
l.logger.Output(2, fmt.Sprintf(format, v...))
}
// Debug prints debug level message.
func (l *Logger) Debug(v ...interface{}) {
if Debug < l.level {
return
}
l.logger.SetPrefix("D ")
l.logger.Output(2, fmt.Sprint(v...))
}
// Debugf prints debug level message with format.
func (l *Logger) Debugf(format string, v ...interface{}) {
if Debug < l.level {
return
}
l.logger.SetPrefix("D ")
l.logger.Output(2, fmt.Sprintf(format, v...))
}
// Info prints info level message.
func (l *Logger) Info(v ...interface{}) {
if Info < l.level {
return
}
l.logger.SetPrefix("I ")
l.logger.Output(2, fmt.Sprint(v...))
}
// Infof prints info level message with format.
func (l *Logger) Infof(format string, v ...interface{}) {
if Info < l.level {
return
}
l.logger.SetPrefix("I ")
l.logger.Output(2, fmt.Sprintf(format, v...))
}
// Warn prints warning level message.
func (l *Logger) Warn(v ...interface{}) {
if Warn < l.level {
return
}
l.logger.SetPrefix("W ")
l.logger.Output(2, fmt.Sprint(v...))
}
// Warn prints warning level message with format.
func (l *Logger) Warnf(format string, v ...interface{}) {
if Warn < l.level {
return
}
l.logger.SetPrefix("W ")
l.logger.Output(2, fmt.Sprintf(format, v...))
}
// Error prints error level message.
func (l *Logger) Error(v ...interface{}) {
if Error < l.level {
return
}
l.logger.SetPrefix("E ")
l.logger.Output(2, fmt.Sprint(v...))
}
// Errorf prints error level message with format.
func (l *Logger) Errorf(format string, v ...interface{}) {
if Error < l.level {
return
}
l.logger.SetPrefix("E ")
l.logger.Output(2, fmt.Sprintf(format, v...))
}

View File

@ -1,169 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"os"
"testing"
)
// Logger.
var logger = NewLogger(os.Stdout)
func TestSetLevel(t *testing.T) {
SetLevel("trace")
}
func TestTrace(t *testing.T) {
logger.SetLevel("trace")
logger.Trace("trace")
logger.SetLevel("off")
logger.Trace("trace")
}
func TestTracef(t *testing.T) {
logger.SetLevel("trace")
logger.Tracef("tracef")
logger.SetLevel("off")
logger.Tracef("tracef")
}
func TestDebug(t *testing.T) {
logger.SetLevel("debug")
logger.Debug("debug")
logger.SetLevel("off")
logger.Debug("debug")
}
func TestDebugf(t *testing.T) {
logger.SetLevel("debug")
logger.Debugf("debugf")
logger.SetLevel("off")
logger.Debug("debug")
}
func TestInfo(t *testing.T) {
logger.SetLevel("info")
logger.Info("info")
logger.SetLevel("off")
logger.Info("info")
}
func TestInfof(t *testing.T) {
logger.SetLevel("info")
logger.Infof("infof")
logger.SetLevel("off")
logger.Infof("infof")
}
func TestWarn(t *testing.T) {
logger.SetLevel("warn")
logger.Warn("warn")
logger.SetLevel("off")
logger.Warn("warn")
}
func TestWarnf(t *testing.T) {
logger.SetLevel("warn")
logger.Warnf("warnf")
logger.SetLevel("off")
logger.Warnf("warnf")
}
func TestError(t *testing.T) {
logger.SetLevel("error")
logger.Error("error")
logger.SetLevel("off")
logger.Error("error")
}
func TestErrorf(t *testing.T) {
logger.SetLevel("error")
logger.Errorf("errorf")
logger.SetLevel("off")
logger.Errorf("errorf")
}
func TestGetLevel(t *testing.T) {
if getLevel("trace") != Trace {
t.FailNow()
return
}
if getLevel("debug") != Debug {
t.FailNow()
return
}
if getLevel("info") != Info {
t.FailNow()
return
}
if getLevel("warn") != Warn {
t.FailNow()
return
}
if getLevel("error") != Error {
t.FailNow()
return
}
}
func TestLoggerSetLevel(t *testing.T) {
logger.SetLevel("trace")
if logger.level != Trace {
t.FailNow()
return
}
}
func TestIsTraceEnabled(t *testing.T) {
logger.SetLevel("trace")
if !logger.IsTraceEnabled() {
t.FailNow()
return
}
}
func TestIsDebugEnabled(t *testing.T) {
logger.SetLevel("debug")
if !logger.IsDebugEnabled() {
t.FailNow()
return
}
}
func TestIsWarnEnabled(t *testing.T) {
logger.SetLevel("warn")
if !logger.IsWarnEnabled() {
t.FailNow()
return
}
}

282
main.go
View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -23,170 +23,142 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/editor"
"github.com/b3log/wide/event"
"github.com/b3log/wide/file"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/log"
"github.com/b3log/wide/notification"
"github.com/b3log/wide/output"
"github.com/b3log/wide/playground"
"github.com/b3log/wide/scm/git"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/editor"
"github.com/88250/wide/event"
"github.com/88250/wide/file"
"github.com/88250/wide/i18n"
"github.com/88250/wide/notification"
"github.com/88250/wide/output"
"github.com/88250/wide/playground"
"github.com/88250/wide/session"
)
// Logger
var logger *log.Logger
var logger *gulu.Logger
// The only one init function in Wide.
func init() {
confPath := flag.String("conf", "conf/wide.json", "path of wide.json")
confIP := flag.String("ip", "", "this will overwrite Wide.IP if specified")
confPort := flag.String("port", "", "this will overwrite Wide.Port if specified")
confData := flag.String("data", "", "path of data dir")
confServer := flag.String("server", "", "this will overwrite Wide.Server if specified")
confLogLevel := flag.String("log_level", "", "this will overwrite Wide.LogLevel if specified")
confStaticServer := flag.String("static_server", "", "this will overwrite Wide.StaticServer if specified")
confContext := flag.String("context", "", "this will overwrite Wide.Context if specified")
confChannel := flag.String("channel", "", "this will overwrite Wide.Channel if specified")
confStat := flag.Bool("stat", false, "whether report statistics periodically")
confDocker := flag.Bool("docker", false, "whether run in a docker container")
confPlayground := flag.String("playground", "", "this will overwrite Wide.Playground if specified")
confReadOnly := flag.String("readonly", "", "this will overrite Wide.ReadOnly if specified")
confSiteStatCode := flag.String("site_stat_code", "", "this will overrite Wide.SiteStatCode if specified")
flag.Parse()
log.SetLevel("warn")
logger = log.NewLogger(os.Stdout)
gulu.Log.SetLevel("warn")
logger = gulu.Log.NewLogger(os.Stdout)
wd := util.OS.Pwd()
if strings.HasPrefix(wd, os.TempDir()) {
logger.Error("Don't run Wide in OS' temp directory or with `go run`")
os.Exit(-1)
}
//wd := gulu.OS.Pwd()
//if strings.HasPrefix(wd, os.TempDir()) {
// logger.Error("Don't run Wide in OS' temp directory or with `go run`")
//
// os.Exit(-1)
//}
i18n.Load()
event.Load()
conf.Load(*confPath, *confIP, *confPort, *confServer, *confLogLevel, *confStaticServer, *confContext, *confChannel,
*confPlayground, *confDocker)
conf.Load(*confPath, *confData, *confServer, *confLogLevel, *confReadOnly, template.HTML(*confSiteStatCode))
conf.FixedTimeCheckEnv()
session.FixedTimeSave()
session.FixedTimeRelease()
if *confStat {
session.FixedTimeReport()
}
logger.Debug("host ["+runtime.Version()+", "+runtime.GOOS+"_"+runtime.GOARCH+"], cross-compilation ",
util.Go.GetCrossPlatforms())
logger.Debug("host [" + runtime.Version() + ", " + runtime.GOOS + "_" + runtime.GOARCH + "]")
}
// Main.
func main() {
runtime.GOMAXPROCS(conf.Wide.MaxProcs)
initMime()
handleSignal()
// IDE
http.HandleFunc(conf.Wide.Context+"/", handlerGzWrapper(indexHandler))
http.HandleFunc(conf.Wide.Context+"/start", handlerWrapper(startHandler))
http.HandleFunc(conf.Wide.Context+"/about", handlerWrapper(aboutHandler))
http.HandleFunc(conf.Wide.Context+"/keyboard_shortcuts", handlerWrapper(keyboardShortcutsHandler))
http.HandleFunc("/", handlerGzWrapper(indexHandler))
http.HandleFunc("/start", handlerWrapper(startHandler))
http.HandleFunc("/about", handlerWrapper(aboutHandler))
http.HandleFunc("/keyboard_shortcuts", handlerWrapper(keyboardShortcutsHandler))
// static resources
http.Handle(conf.Wide.Context+"/static/", http.StripPrefix(conf.Wide.Context+"/static/", http.FileServer(http.Dir("static"))))
serveSingle("/favicon.ico", "./static/favicon.ico")
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
http.Handle("/static/users/", http.StripPrefix("/static/", http.FileServer(http.Dir(conf.Wide.Data+"/static"))))
serveSingle("/favicon.ico", "./static/images/favicon.png")
// workspaces
for _, user := range conf.Users {
http.Handle(conf.Wide.Context+"/workspace/"+user.Name+"/",
http.StripPrefix(conf.Wide.Context+"/workspace/"+user.Name+"/", http.FileServer(http.Dir(user.GetWorkspace()))))
}
// oauth
http.HandleFunc("/login/redirect", session.LoginRedirectHandler)
http.HandleFunc("/login/callback", session.LoginCallbackHandler)
// session
http.HandleFunc(conf.Wide.Context+"/session/ws", handlerWrapper(session.WSHandler))
http.HandleFunc(conf.Wide.Context+"/session/save", handlerWrapper(session.SaveContentHandler))
http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
http.HandleFunc("/session/save", handlerWrapper(session.SaveContentHandler))
// run
http.HandleFunc(conf.Wide.Context+"/build", handlerWrapper(output.BuildHandler))
http.HandleFunc(conf.Wide.Context+"/run", handlerWrapper(output.RunHandler))
http.HandleFunc(conf.Wide.Context+"/stop", handlerWrapper(output.StopHandler))
http.HandleFunc(conf.Wide.Context+"/go/test", handlerWrapper(output.GoTestHandler))
http.HandleFunc(conf.Wide.Context+"/go/vet", handlerWrapper(output.GoVetHandler))
http.HandleFunc(conf.Wide.Context+"/go/get", handlerWrapper(output.GoGetHandler))
http.HandleFunc(conf.Wide.Context+"/go/install", handlerWrapper(output.GoInstallHandler))
http.HandleFunc(conf.Wide.Context+"/output/ws", handlerWrapper(output.WSHandler))
http.HandleFunc("/build", handlerWrapper(output.BuildHandler))
http.HandleFunc("/run", handlerWrapper(output.RunHandler))
http.HandleFunc("/stop", handlerWrapper(output.StopHandler))
http.HandleFunc("/go/test", handlerWrapper(output.GoTestHandler))
http.HandleFunc("/go/vet", handlerWrapper(output.GoVetHandler))
http.HandleFunc("/go/install", handlerWrapper(output.GoInstallHandler))
http.HandleFunc("/output/ws", handlerWrapper(output.WSHandler))
// cross-compilation
http.HandleFunc(conf.Wide.Context+"/cross", handlerWrapper(output.CrossCompilationHandler))
http.HandleFunc("/cross", handlerWrapper(output.CrossCompilationHandler))
// file tree
http.HandleFunc(conf.Wide.Context+"/files", handlerWrapper(file.GetFilesHandler))
http.HandleFunc(conf.Wide.Context+"/file/refresh", handlerWrapper(file.RefreshDirectoryHandler))
http.HandleFunc(conf.Wide.Context+"/file", handlerWrapper(file.GetFileHandler))
http.HandleFunc(conf.Wide.Context+"/file/save", handlerWrapper(file.SaveFileHandler))
http.HandleFunc(conf.Wide.Context+"/file/new", handlerWrapper(file.NewFileHandler))
http.HandleFunc(conf.Wide.Context+"/file/remove", handlerWrapper(file.RemoveFileHandler))
http.HandleFunc(conf.Wide.Context+"/file/rename", handlerWrapper(file.RenameFileHandler))
http.HandleFunc(conf.Wide.Context+"/file/search/text", handlerWrapper(file.SearchTextHandler))
http.HandleFunc(conf.Wide.Context+"/file/find/name", handlerWrapper(file.FindHandler))
http.HandleFunc("/files", handlerWrapper(file.GetFilesHandler))
http.HandleFunc("/file/refresh", handlerWrapper(file.RefreshDirectoryHandler))
http.HandleFunc("/file", handlerWrapper(file.GetFileHandler))
http.HandleFunc("/file/save", handlerWrapper(file.SaveFileHandler))
http.HandleFunc("/file/new", handlerWrapper(file.NewFileHandler))
http.HandleFunc("/file/remove", handlerWrapper(file.RemoveFileHandler))
http.HandleFunc("/file/rename", handlerWrapper(file.RenameFileHandler))
http.HandleFunc("/file/search/text", handlerWrapper(file.SearchTextHandler))
http.HandleFunc("/file/find/name", handlerWrapper(file.FindHandler))
// outline
http.HandleFunc(conf.Wide.Context+"/outline", handlerWrapper(file.GetOutlineHandler))
http.HandleFunc("/outline", handlerWrapper(file.GetOutlineHandler))
// file export/import
http.HandleFunc(conf.Wide.Context+"/file/zip/new", handlerWrapper(file.CreateZipHandler))
http.HandleFunc(conf.Wide.Context+"/file/zip", handlerWrapper(file.GetZipHandler))
http.HandleFunc(conf.Wide.Context+"/file/upload", handlerWrapper(file.UploadHandler))
http.HandleFunc(conf.Wide.Context+"/file/decompress", handlerWrapper(file.DecompressHandler))
// file export
http.HandleFunc("/file/zip/new", handlerWrapper(file.CreateZipHandler))
http.HandleFunc("/file/zip", handlerWrapper(file.GetZipHandler))
// editor
http.HandleFunc(conf.Wide.Context+"/editor/ws", handlerWrapper(editor.WSHandler))
http.HandleFunc(conf.Wide.Context+"/go/fmt", handlerWrapper(editor.GoFmtHandler))
http.HandleFunc(conf.Wide.Context+"/autocomplete", handlerWrapper(editor.AutocompleteHandler))
http.HandleFunc(conf.Wide.Context+"/exprinfo", handlerWrapper(editor.GetExprInfoHandler))
http.HandleFunc(conf.Wide.Context+"/find/decl", handlerWrapper(editor.FindDeclarationHandler))
http.HandleFunc(conf.Wide.Context+"/find/usages", handlerWrapper(editor.FindUsagesHandler))
// shell
// http.HandleFunc(conf.Wide.Context+"/shell/ws", handlerWrapper(shell.WSHandler))
// http.HandleFunc(conf.Wide.Context+"/shell", handlerWrapper(shell.IndexHandler))
http.HandleFunc("/go/fmt", handlerWrapper(editor.GoFmtHandler))
http.HandleFunc("/autocomplete", handlerWrapper(editor.AutocompleteHandler))
http.HandleFunc("/exprinfo", handlerWrapper(editor.GetExprInfoHandler))
http.HandleFunc("/find/decl", handlerWrapper(editor.FindDeclarationHandler))
http.HandleFunc("/find/usages", handlerWrapper(editor.FindUsagesHandler))
// notification
http.HandleFunc(conf.Wide.Context+"/notification/ws", handlerWrapper(notification.WSHandler))
http.HandleFunc("/notification/ws", handlerWrapper(notification.WSHandler))
// user
http.HandleFunc(conf.Wide.Context+"/login", handlerWrapper(session.LoginHandler))
http.HandleFunc(conf.Wide.Context+"/logout", handlerWrapper(session.LogoutHandler))
http.HandleFunc(conf.Wide.Context+"/signup", handlerWrapper(session.SignUpUserHandler))
http.HandleFunc(conf.Wide.Context+"/preference", handlerWrapper(session.PreferenceHandler))
http.HandleFunc("/login", handlerWrapper(session.LoginHandler))
http.HandleFunc("/logout", handlerWrapper(session.LogoutHandler))
http.HandleFunc("/preference", handlerWrapper(session.PreferenceHandler))
// playground
http.HandleFunc(conf.Wide.Context+"/playground", handlerWrapper(playground.IndexHandler))
http.HandleFunc(conf.Wide.Context+"/playground/", handlerWrapper(playground.IndexHandler))
http.HandleFunc(conf.Wide.Context+"/playground/ws", handlerWrapper(playground.WSHandler))
http.HandleFunc(conf.Wide.Context+"/playground/save", handlerWrapper(playground.SaveHandler))
http.HandleFunc(conf.Wide.Context+"/playground/short-url", handlerWrapper(playground.ShortURLHandler))
http.HandleFunc(conf.Wide.Context+"/playground/build", handlerWrapper(playground.BuildHandler))
http.HandleFunc(conf.Wide.Context+"/playground/run", handlerWrapper(playground.RunHandler))
http.HandleFunc(conf.Wide.Context+"/playground/stop", handlerWrapper(playground.StopHandler))
http.HandleFunc(conf.Wide.Context+"/playground/autocomplete", handlerWrapper(playground.AutocompleteHandler))
http.HandleFunc("/playground", handlerWrapper(playground.IndexHandler))
http.HandleFunc("/playground/", handlerWrapper(playground.IndexHandler))
http.HandleFunc("/playground/ws", handlerWrapper(playground.WSHandler))
http.HandleFunc("/playground/save", handlerWrapper(playground.SaveHandler))
http.HandleFunc("/playground/build", handlerWrapper(playground.BuildHandler))
http.HandleFunc("/playground/run", handlerWrapper(playground.RunHandler))
http.HandleFunc("/playground/stop", handlerWrapper(playground.StopHandler))
http.HandleFunc("/playground/autocomplete", handlerWrapper(playground.AutocompleteHandler))
// git
http.HandleFunc(conf.Wide.Context+"/git/clone", handlerWrapper(git.CloneHandler))
logger.Infof("Wide is running [%s]", conf.Wide.Server)
logger.Infof("Wide is running [%s]", conf.Wide.Server+conf.Wide.Context)
err := http.ListenAndServe(conf.Wide.Server, nil)
err := http.ListenAndServe("0.0.0.0:7070", nil)
if err != nil {
logger.Error(err)
}
@ -200,51 +172,45 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
return
}
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
username := httpSession.Values["username"].(string)
if "playground" == username { // reserved user for Playground
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
uid := httpSession.Values["uid"].(string)
if "playground" == uid { // reserved user for Playground
http.Redirect(w, r, "/login", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
user := conf.GetUser(username)
user := conf.GetUser(uid)
if nil == user {
logger.Warnf("Not found user [%s]", username)
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
locale := user.Locale
wideSessions := session.WideSessions.GetByUsername(username)
wideSessions := session.WideSessions.GetByUserId(uid)
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"username": username, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent,
"uid": uid, "sid": session.WideSessions.GenId(), "latestSessionContent": user.LatestSessionContent,
"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer,
"user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": util.Go.GetCrossPlatforms()}
"user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": []string{"darwin_amd64", "linux_amd64", "windows_amd64"}}
logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions))
logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions))
t, err := template.ParseFiles("views/index.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -252,6 +218,22 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
t.Execute(w, model)
}
// handleSignal handles system signal for graceful shutdown.
func handleSignal() {
go func() {
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
s := <-c
logger.Tracef("Got signal [%s]", s)
session.SaveOnlineUsers()
logger.Tracef("Saved all online user, exit")
os.Exit(0)
}()
}
// serveSingle registers the handler function for the given pattern and filename.
func serveSingle(pattern string, filename string) {
http.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
@ -261,22 +243,20 @@ func serveSingle(pattern string, filename string) {
// startHandler handles request of start page.
func startHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/s", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
userWorkspace := conf.GetUserWorkspace(username)
uid := httpSession.Values["uid"].(string)
user := conf.GetUser(uid)
locale := user.Locale
userWorkspace := conf.GetUserWorkspace(uid)
sid := r.URL.Query()["sid"][0]
wSession := session.WideSessions.Get(sid)
@ -285,13 +265,13 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
}
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"username": username, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid}
"uid": uid, "workspace": userWorkspace, "ver": conf.WideVersion, "sid": sid, "username": user.Name}
t, err := template.ParseFiles("views/start.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -301,21 +281,18 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
// keyboardShortcutsHandler handles request of keyboard shortcuts page.
func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale}
@ -323,7 +300,7 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -333,21 +310,18 @@ func keyboardShortcutsHandler(w http.ResponseWriter, r *http.Request) {
// aboutHandle handles request of about page.
func aboutHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version()}
@ -356,7 +330,7 @@ func aboutHandler(w http.ResponseWriter, r *http.Request) {
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
@ -435,7 +409,7 @@ func stopwatch(handler func(w http.ResponseWriter, r *http.Request)) func(w http
// panicRecover wraps the panic recover process.
func panicRecover(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
defer util.Recover()
defer gulu.Panic.Recover(nil)
handler(w, r)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -21,12 +21,12 @@ import (
"strconv"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/event"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/event"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
"github.com/88250/wide/util"
"github.com/gorilla/websocket"
)
@ -40,7 +40,7 @@ const (
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Notification represents a notification.
type Notification struct {
@ -62,9 +62,9 @@ func event2Notification(e *event.Event) {
return
}
httpSession, _ := session.HTTPSession.Get(wsChannel.Request, "wide-session")
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
httpSession, _ := session.HTTPSession.Get(wsChannel.Request, session.CookieName)
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var notification *Notification

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -17,94 +17,132 @@ package output
import (
"bufio"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
)
// BuildHandler handles request of building.
func BuildHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
user := conf.GetUser(uid)
locale := user.Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
sid := args["sid"].(string)
filePath := args["file"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if gulu.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
curDir := filepath.Dir(filePath)
fout, err := os.Create(filePath)
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
code := args["code"].(string)
fout.WriteString(code)
if err := fout.Close(); nil != err {
if _, err := fout.WriteString(code); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
fout.Close()
channelRet := map[string]interface{}{}
if nil != session.OutputWS[sid] {
// display "START [go build]" in front-end browser
msg := i18n.Get(locale, "start-build").(string)
msg = strings.Replace(msg, "build]", "build "+fmt.Sprint(user.BuildArgs(runtime.GOOS))+"]", 1)
channelRet["output"] = "<span class='start-build'>" + msg + "</span>\n"
channelRet["cmd"] = "start-build"
wsChannel := session.OutputWS[sid]
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
var goModCmd *exec.Cmd
if !gulu.File.IsExist(filepath.Join(curDir, "go.mod")) {
curDirName := filepath.Base(curDir)
goModCmd = exec.Command("go", "mod", "init", curDirName)
} else {
goModCmd = exec.Command("go", "mod", "tidy")
}
goModCmd.Dir = curDir
setCmdEnv(goModCmd, uid)
outputBytes, err := goModCmd.CombinedOutput()
output := string(outputBytes)
if nil != err && strings.Contains(output, "go.mod already exists") {
logger.Error(err.Error() + ": " + output)
result.Code = -1
return
}
var goBuildArgs []string
goBuildArgs = append(goBuildArgs, "build")
goBuildArgs = append(goBuildArgs, user.BuildArgs(runtime.GOOS)...)
//if !gulu.Str.Contains("-i", goBuildArgs) {
// goBuildArgs = append(goBuildArgs, "-i")
//}
cmd := exec.Command("go", goBuildArgs...)
cmd.Dir = curDir
setCmdEnv(cmd, uid)
suffix := ""
if util.OS.IsWindows() {
if gulu.OS.IsWindows() {
suffix = ".exe"
}
cmd := exec.Command("go", "build")
cmd.Dir = curDir
setCmdEnv(cmd, username)
executable := filepath.Base(curDir) + suffix
executable = filepath.Join(curDir, executable)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -112,90 +150,108 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
if !result.Succ {
if 0 != result.Code {
return
}
channelRet := map[string]interface{}{}
if nil != session.OutputWS[sid] {
// display "START [go build]" in front-end browser
channelRet["output"] = "<span class='start-build'>" + i18n.Get(locale, "start-build").(string) + "</span>\n"
channelRet["cmd"] = "start-build"
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Code = -1
return
}
channelRet["cmd"] = "build"
channelRet["executable"] = executable
outReader := bufio.NewReader(stdout)
/////////
go func() {
defer gulu.Panic.Recover(nil)
for {
wsChannel := session.OutputWS[sid]
if nil == wsChannel {
break
}
line, err := outReader.ReadString('\n')
if io.EOF == err {
break
}
_, ok := err.(*os.PathError)
if ok {
// 构建时报 “read |0: file already closed” https://github.com/b3log/wide/issues/363
break
}
if nil != err {
logger.Warnf("%#v", err)
break
}
channelRet["output"] = line
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
}()
errReader := bufio.NewReader(stderr)
var lines []string
for {
wsChannel := session.OutputWS[sid]
if nil == wsChannel {
break
}
line, err := errReader.ReadString('\n')
if io.EOF == err {
break
}
lines = append(lines, line)
if nil != err {
logger.Warn(err)
break
}
// path process
errOutWithPath := parsePath(curDir, line)
channelRet["output"] = "<span class='stderr'>" + errOutWithPath + "</span>"
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
return
}
go func(runningId int) {
defer util.Recover()
defer cmd.Wait()
// logger.Debugf("User [%s, %s] is building [id=%d, dir=%s]", username, sid, runningId, curDir)
// read all
buf, _ := ioutil.ReadAll(reader)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "build"
channelRet["executable"] = executable
if 0 == len(buf) { // build success
if nil == cmd.Wait() {
channelRet["nextCmd"] = args["nextCmd"]
channelRet["output"] = "<span class='build-succ'>" + i18n.Get(locale, "build-succ").(string) + "</span>\n"
go func() { // go install, for subsequent gocode lib-path
defer util.Recover()
cmd := exec.Command("go", "install")
cmd.Dir = curDir
setCmdEnv(cmd, username)
out, _ := cmd.CombinedOutput()
if len(out) > 0 {
logger.Warn(string(out))
}
}()
} else { // build error
// build gutter lint
errOut := string(buf)
lines := strings.Split(errOut, "\n")
// path process
var errOutWithPath string
for _, line := range lines {
errOutWithPath += parsePath(curDir, line) + "\n"
}
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n" +
"<span class='stderr'>" + errOutWithPath + "</span>"
} else {
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n"
// lint process
if lines[0][0] == '#' {
lines = lines[1:] // skip the first line
}
@ -203,7 +259,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
lints := []*Lint{}
for _, line := range lines {
if len(line) < 1 {
if len(line) < 1 || !strings.Contains(line, ":") {
continue
}
@ -234,7 +290,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
}
lint := &Lint{
File: filepath.Join(curDir, file),
File: filepath.ToSlash(filepath.Join(curDir, file)),
LineNo: lineNo - 1,
Severity: lintSeverityError,
Msg: msg,
@ -246,17 +302,14 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
channelRet["lints"] = lints
}
if nil != session.OutputWS[sid] {
// logger.Debugf("User [%s, %s] 's build [id=%d, dir=%s] has done", username, sid, runningId, curDir)
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil == wsChannel {
return
}
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
}
wsChannel.Refresh()
}
}(rand.Int())
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -26,31 +26,37 @@ import (
"strconv"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
)
// CrossCompilationHandler handles request of cross compilation.
func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -58,7 +64,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
sid := args["sid"].(string)
filePath := args["path"].(string)
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
if gulu.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -75,10 +81,15 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
suffix = ".exe"
}
cmd := exec.Command("go", "build")
user := conf.GetUser(uid)
goBuildArgs := []string{}
goBuildArgs = append(goBuildArgs, "build")
goBuildArgs = append(goBuildArgs, user.BuildArgs(goos)...)
cmd := exec.Command("go", goBuildArgs...)
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
for i, env := range cmd.Env {
if strings.HasPrefix(env, "GOOS=") {
@ -101,7 +112,7 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -109,12 +120,12 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
if !result.Succ {
if 0 != result.Code {
return
}
@ -141,17 +152,15 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
go func(runningId int) {
defer util.Recover()
defer gulu.Panic.Recover(nil)
defer cmd.Wait()
// logger.Debugf("User [%s, %s] is building [id=%d, dir=%s]", username, sid, runningId, curDir)
// read all
buf, _ := ioutil.ReadAll(reader)
@ -230,8 +239,6 @@ func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
}
if nil != session.OutputWS[sid] {
// logger.Debugf("User [%s, %s] 's build [id=%d, dir=%s] has done", username, sid, runningId, curDir)
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {

View File

@ -1,146 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package output
import (
"bufio"
"encoding/json"
"io"
"io/ioutil"
"math/rand"
"net/http"
"os/exec"
"path/filepath"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
)
// GoGetHandler handles request of go get.
func GoGetHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
sid := args["sid"].(string)
filePath := args["file"].(string)
curDir := filepath.Dir(filePath)
cmd := exec.Command("go", "get")
cmd.Dir = curDir
setCmdEnv(cmd, username)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
return
}
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
return
}
if !result.Succ {
return
}
channelRet := map[string]interface{}{}
if nil != session.OutputWS[sid] {
// display "START [go get]" in front-end browser
channelRet["output"] = "<span class='start-get'>" + i18n.Get(locale, "start-get").(string) + "</span>\n"
channelRet["cmd"] = "start-get"
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
return
}
go func(runningId int) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [go get] [runningId=%d]", username, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go get"
// read all
buf, _ := ioutil.ReadAll(reader)
if 0 != len(buf) {
logger.Debugf("User [%s, %s] 's [go get] [runningId=%d] has done (with error)", username, sid, runningId)
channelRet["output"] = "<span class='get-error'>" + i18n.Get(locale, "get-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go get] [runningId=%d] has done", username, sid, runningId)
channelRet["output"] = "<span class='get-succ'>" + i18n.Get(locale, "get-succ").(string) + "</span>\n"
}
if nil != session.OutputWS[sid] {
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
}
wsChannel.Refresh()
}
}(rand.Int())
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -26,31 +26,37 @@ import (
"strconv"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
)
// GoInstallHandler handles request of go install.
func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -63,14 +69,14 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "install")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
logger.Debugf("go install %s", curDir)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -78,12 +84,12 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
if !result.Succ {
if 0 != result.Code {
return
}
@ -110,16 +116,16 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
go func(runningId int) {
defer util.Recover()
defer gulu.Panic.Recover(nil)
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", username, sid, runningId, curDir)
logger.Debugf("User [%s, %s] is running [go install] [id=%d, dir=%s]", uid, sid, runningId, curDir)
// read all
buf, _ := ioutil.ReadAll(reader)
@ -183,7 +189,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
}
if nil != session.OutputWS[sid] {
logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", username, sid, runningId, curDir)
logger.Debugf("User [%s, %s] 's running [go install] [id=%d, dir=%s] has done", uid, sid, runningId, curDir)
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)

View File

@ -1,25 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !linux
package output
import (
"os/exec"
)
func SetNamespace(cmd *exec.Cmd) {
// do nothing
}

View File

@ -1,35 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package output
import (
"os/exec"
"syscall"
)
func SetNamespace(cmd *exec.Cmd) {
// XXX: keep move with Go 1.4 and later's
cmd.SysProcAttr = &syscall.SysProcAttr{}
//cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER | syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWIPC | syscall.CLONE_NEWNET
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER /*| syscall.CLONE_NEWNS*/ | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWIPC /*| syscall.CLONE_NEWNET*/
cmd.SysProcAttr.Credential = &syscall.Credential{
Uid: 0,
Gid: 0,
}
cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}
cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{ContainerID: 0, HostID: 1001, Size: 1}}
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -25,10 +25,10 @@ import (
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/session"
"github.com/88250/wide/util"
"github.com/gorilla/websocket"
)
@ -38,7 +38,7 @@ const (
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// Lint represents a code lint.
type Lint struct {
@ -93,7 +93,7 @@ func parsePath(curDir, outputLine string) string {
column = parts[2]
}
tagStart := `<span class="path" data-path="` + filepath.Join(curDir, file) + `" data-line="` + line +
tagStart := `<span class="path" data-path="` + filepath.ToSlash(filepath.Join(curDir, file)) + `" data-line="` + line +
`" data-column="` + column + `">`
text := file + ":" + line
if hasColumn {
@ -104,18 +104,29 @@ func parsePath(curDir, outputLine string) string {
return tagStart + text + tagEnd + msgPart
}
func setCmdEnv(cmd *exec.Cmd, username string) {
userWorkspace := conf.GetUserWorkspace(username)
func setCmdEnv(cmd *exec.Cmd, uid string) {
userWorkspace := conf.GetUserWorkspace(uid)
cache, err := os.UserCacheDir()
if nil != err {
logger.Warnf("Get user cache dir failed [" + err.Error() + "]")
cache = os.TempDir()
}
cmd.Env = append(cmd.Env,
"GOPROXY=https://goproxy.cn",
"GO111MODULE=on",
"GOPATH="+userWorkspace,
"GOOS="+runtime.GOOS,
"GOARCH="+runtime.GOARCH,
"GOROOT="+runtime.GOROOT(),
"GOCACHE="+cache,
"PATH="+os.Getenv("PATH"))
if util.OS.IsWindows() {
if gulu.OS.IsWindows() {
// FIXME: for some weird issues on Windows, such as: The requested service provider could not be loaded or initialized.
cmd.Env = append(cmd.Env, os.Environ()...)
} else {
// 编译链接时找不到依赖的动态库 https://github.com/b3log/wide/issues/352
cmd.Env = append(cmd.Env, "LD_LIBRARY_PATH="+os.Getenv("LD_LIBRARY_PATH"))
}
}

View File

@ -1,105 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package output
import (
"os"
"sync"
"github.com/b3log/wide/session"
)
// Type of process set.
type procs map[string][]*os.Process
// Processse of all users.
//
// <sid, []*os.Process>
var Processes = procs{}
// Exclusive lock.
var mutex sync.Mutex
// Add adds the specified process to the user process set.
func (procs *procs) Add(wSession *session.WideSession, proc *os.Process) {
mutex.Lock()
defer mutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
userProcesses = append(userProcesses, proc)
(*procs)[sid] = userProcesses
// bind process with wide session
wSession.SetProcesses(userProcesses)
logger.Tracef("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
}
// Remove removes the specified process from the user process set.
func (procs *procs) Remove(wSession *session.WideSession, proc *os.Process) {
mutex.Lock()
defer mutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
var newProcesses []*os.Process
for i, p := range userProcesses {
if p.Pid == proc.Pid {
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) // remove it
(*procs)[sid] = newProcesses
// bind process with wide session
wSession.SetProcesses(newProcesses)
logger.Tracef("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
return
}
}
}
// Kill kills a process specified by the given pid.
func (procs *procs) Kill(wSession *session.WideSession, pid int) {
mutex.Lock()
defer mutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
for i, p := range userProcesses {
if p.Pid == pid {
if err := p.Kill(); nil != err {
logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.Username, sid, err)
} else {
var newProcesses []*os.Process
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
(*procs)[sid] = newProcesses
// bind process with wide session
wSession.SetProcesses(newProcesses)
logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.Username, sid)
}
return
}
}
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -15,246 +15,16 @@
package output
import (
"bufio"
"encoding/json"
"math/rand"
"github.com/88250/wide/session"
"net/http"
"os/exec"
"path/filepath"
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
)
const (
outputBufMax = 128 // 128 string(rune)
outputTimeout = 100 // 100ms
)
type outputBuf struct {
content string
millisecond int64
}
// RunHandler handles request of executing a binary file.
func RunHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
}
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
}
filePath := args["executable"].(string)
curDir := filepath.Dir(filePath)
cmd := exec.Command(filePath)
cmd.Dir = curDir
if conf.Docker {
SetNamespace(cmd)
}
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
}
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
}
outReader := bufio.NewReader(stdout)
errReader := bufio.NewReader(stderr)
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
}
wsChannel := session.OutputWS[sid]
channelRet := map[string]interface{}{}
if !result.Succ {
if nil != wsChannel {
channelRet["cmd"] = "run-done"
channelRet["output"] = ""
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
return
}
channelRet["pid"] = cmd.Process.Pid
// add the process to user's process set
Processes.Add(wSession, cmd.Process)
go func(runningId int) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath)
// push once for front-end to get the 'run' state and pid
if nil != wsChannel {
channelRet["cmd"] = "run"
channelRet["output"] = ""
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
go func() {
defer util.Recover()
buf := outputBuf{}
for {
wsChannel := session.OutputWS[sid]
if nil == wsChannel {
break
}
r, _, err := outReader.ReadRune()
if nil != err {
// remove the exited process from user's process set
Processes.Remove(wSession, cmd.Process)
logger.Debugf("User [%s, %s] 's running [id=%d, file=%s] has done [stdout %v], ",
wSession.Username, sid, runningId, filePath, err)
channelRet["cmd"] = "run-done"
channelRet["output"] = buf.content
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
break
}
oneRuneStr := string(r)
oneRuneStr = strings.Replace(oneRuneStr, "<", "&lt;", -1)
oneRuneStr = strings.Replace(oneRuneStr, ">", "&gt;", -1)
buf.content += oneRuneStr
now := time.Now().UnixNano() / int64(time.Millisecond)
if 0 == buf.millisecond {
buf.millisecond = now
}
if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" {
channelRet["cmd"] = "run"
channelRet["output"] = buf.content
buf = outputBuf{} // a new buffer
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
}
}()
buf := outputBuf{}
for {
r, _, err := errReader.ReadRune()
wsChannel := session.OutputWS[sid]
if nil != err || nil == wsChannel {
break
}
oneRuneStr := string(r)
oneRuneStr = strings.Replace(oneRuneStr, "<", "&lt;", -1)
oneRuneStr = strings.Replace(oneRuneStr, ">", "&gt;", -1)
buf.content += oneRuneStr
now := time.Now().UnixNano() / int64(time.Millisecond)
if 0 == buf.millisecond {
buf.millisecond = now
}
if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" {
channelRet["cmd"] = "run"
channelRet["output"] = "<span class='stderr'>" + buf.content + "</span>"
buf = outputBuf{} // a new buffer
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
}
}(rand.Int())
session.RunHandler(w, r, session.OutputWS)
}
// StopHandler handles request of stoping a running process.
// StopHandler handles request of stopping a running process.
func StopHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
sid := args["sid"].(string)
pid := int(args["pid"].(float64))
wSession := session.WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
return
}
Processes.Kill(wSession, pid)
session.StopHandler(w, r)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -24,31 +24,31 @@ import (
"os/exec"
"path/filepath"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
)
// GoTestHandler handles request of go test.
func GoTestHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -61,12 +61,12 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "test", "-v")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -74,12 +74,12 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
if !result.Succ {
if 0 != result.Code {
return
}
@ -106,15 +106,15 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
go func(runningId int) {
defer util.Recover()
defer gulu.Panic.Recover(nil)
logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", username, sid, runningId)
logger.Debugf("User [%s, %s] is running [go test] [runningId=%d]", uid, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go test"
@ -126,11 +126,11 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
cmd.Wait()
if !cmd.ProcessState.Success() {
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='test-error'>" + i18n.Get(locale, "test-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go test] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='test-succ'>" + i18n.Get(locale, "test-succ").(string) + "</span>\n" + string(buf)
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -24,31 +24,31 @@ import (
"os/exec"
"path/filepath"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
)
// GoVetHandler handles request of go vet.
func GoVetHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -61,12 +61,12 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
cmd := exec.Command("go", "vet", ".")
cmd.Dir = curDir
setCmdEnv(cmd, username)
setCmdEnv(cmd, uid)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -74,12 +74,12 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
if !result.Succ {
if 0 != result.Code {
return
}
@ -106,15 +106,15 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
go func(runningId int) {
defer util.Recover()
defer gulu.Panic.Recover(nil)
logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", username, sid, runningId)
logger.Debugf("User [%s, %s] is running [go vet] [runningId=%d]", uid, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "go vet"
@ -126,11 +126,11 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
cmd.Wait()
if !cmd.ProcessState.Success() {
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='vet-error'>" + i18n.Get(locale, "vet-error").(string) + "</span>\n" + string(buf)
} else {
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", username, sid, runningId)
logger.Debugf("User [%s, %s] 's running [go vet] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='vet-succ'>" + i18n.Get(locale, "vet-succ").(string) + "</span>\n" + string(buf)
}

3773
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,36 @@
{
"name": "wide",
"version": "1.4.0",
"description": "A Web-based IDE for Teams using Go programming language/Golang.",
"version": "1.6.0",
"description": "A Web-based Go IDE , do your development anytime, anywhere.",
"homepage": "https://wide.b3log.org",
"repository": {
"type": "git",
"url": "git://github.com/b3log/wide.git"
"url": "git://github.com/88250/wide.git"
},
"bugs": {
"url": "https://github.com/b3log/wide/issues"
"url": "https://github.com/88250/wide/issues"
},
"license": "Apache License",
"private": true,
"author": "Daniel <dl882509@gmail.com> (http://88250.b3log.org) & Vanessa <lly219@gmail.com> (http://vanessa.b3log.org)",
"author": "Daniel <d@b3log.org> (http://88250.b3log.org) & Vanessa <v@b3log.org> (http://vanessa.b3log.org)",
"maintainers": [
{
"name": "Daniel",
"email": "dl88250@gmail.com"
"email": "d@b3log.org"
},
{
"name": "Vanessa",
"email": "lly219@gmail.com"
"email": "v@b3log.org"
}
],
"scripts": {
"build": "gulp"
},
"devDependencies": {
"gulp": "3.9.0",
"gulp-concat": "2.6.0",
"gulp-minify-css": "1.2.2",
"gulp-uglify": "1.5.1",
"gulp-sourcemaps": "1.6.0"
"gulp": "^4.0.2",
"gulp-clean-css": "^4.2.0",
"gulp-concat": "^2.6.1",
"gulp-sourcemaps": "^2.6.5",
"gulp-uglify": "^3.0.1"
}
}

12
pkg.sh
View File

@ -24,7 +24,7 @@ export GOARCH=amd64
echo wide-${ver}-${GOOS}-${GOARCH}.tar.gz
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
tar zcf ${target}/wide-${ver}-${GOOS}-${GOARCH}.tar.gz ${list} gotools gocode wide --exclude-vcs --exclude='conf/*.go' --exclude='i18n/*.go'
rm -f wide gotools gocode
@ -33,7 +33,7 @@ export GOARCH=386
echo wide-${ver}-${GOOS}-${GOARCH}.tar.gz
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
tar zcf ${target}/wide-${ver}-${GOOS}-${GOARCH}.tar.gz ${list} gotools gocode wide --exclude-vcs --exclude='conf/*.go' --exclude='i18n/*.go'
rm -f wide gotools gocode
@ -45,7 +45,7 @@ export GOARCH=amd64
echo wide-${ver}-${GOOS}-${GOARCH}.tar.gz
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
tar zcf ${target}/wide-${ver}-${GOOS}-${GOARCH}.tar.gz ${list} gotools gocode wide --exclude-vcs --exclude='conf/*.go' --exclude='i18n/*.go'
rm -f wide gotools gocode
@ -54,7 +54,7 @@ export GOARCH=386
echo wide-${ver}-${GOOS}-${GOARCH}.tar.gz
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
tar zcf ${target}/wide-${ver}-${GOOS}-${GOARCH}.tar.gz ${list} gotools gocode wide --exclude-vcs --exclude='conf/*.go' --exclude='i18n/*.go'
rm -f wide gotools gocode
@ -66,7 +66,7 @@ export GOARCH=amd64
echo wide-${ver}-${GOOS}-${GOARCH}.zip
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
zip -r -q ${target}/wide-${ver}-${GOOS}-${GOARCH}.zip ${list} gotools.exe gocode.exe wide.exe --exclude=conf/*.go --exclude=i18n/*.go
rm -f wide.exe gotools.exe gocode.exe
@ -75,6 +75,6 @@ export GOARCH=386
echo wide-${ver}-${GOOS}-${GOARCH}.zip
go build
go build github.com/visualfc/gotools
go build github.com/nsf/gocode
go build github.com/stamblerre/gocode
zip -r -q ${target}/wide-${ver}-${GOOS}-${GOARCH}.zip ${list} gotools.exe gocode.exe wide.exe --exclude=conf/*.go --exclude=i18n/*.go
rm -f wide.exe gotools.exe gocode.exe

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -18,26 +18,32 @@ import (
"bytes"
"encoding/json"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/session"
)
// AutocompleteHandler handles request of code autocompletion.
func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
var args map[string]interface{}
if conf.Wide.ReadOnly {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
session, _ := session.HTTPSession.Get(r, "wide-session")
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
@ -48,20 +54,28 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
line := int(args["cursorLine"].(float64))
ch := int(args["cursorCh"].(float64))
file, err := os.Create("wide_autocomplete_" + gulu.Rand.String(16) + ".go")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
file.WriteString(code)
file.Close()
path := file.Name()
defer os.Remove(path)
offset := getCursorOffset(code, line, ch)
argv := []string{"-f=json", "autocomplete", strconv.Itoa(offset)}
gocode := util.Go.GetExecutableInGOBIN("gocode")
argv := []string{"-f=json", "--in=" + path, "autocomplete", strconv.Itoa(offset)}
gocode := gulu.Go.GetExecutableInGOBIN("gocode")
cmd := exec.Command(gocode, argv...)
stdin, _ := cmd.StdinPipe()
stdin.Write([]byte(code))
stdin.Close()
output, err := cmd.CombinedOutput()
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -22,17 +22,23 @@ import (
"path/filepath"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/session"
)
// BuildHandler handles request of Playground building.
func BuildHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
@ -42,23 +48,23 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
fileName := args["fileName"].(string)
filePath := filepath.Clean(conf.Wide.Playground + "/" + fileName)
filePath := filepath.Clean(conf.Wide.Data + "/playground/" + fileName)
suffix := ""
if util.OS.IsWindows() {
if gulu.OS.IsWindows() {
suffix = ".exe"
}
data := map[string]interface{}{}
result.Data = &data
executable := filepath.Clean(conf.Wide.Playground + "/" + strings.Replace(fileName, ".go", suffix, -1))
executable := filepath.Clean(conf.Wide.Data + "/playground/" + strings.Replace(fileName, ".go", suffix, -1))
cmd := exec.Command("go", "build", "-o", executable, filePath)
out, err := cmd.CombinedOutput()
@ -66,7 +72,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
data["output"] = template.HTML(string(out))
if nil != err {
result.Succ = false
result.Code = -1
return
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -23,19 +23,24 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/session"
)
// SaveHandler handles request of Playground code save.
func SaveHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
@ -45,7 +50,7 @@ func SaveHandler(w http.ResponseWriter, r *http.Request) {
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -58,7 +63,7 @@ func SaveHandler(w http.ResponseWriter, r *http.Request) {
stdin, err := cmd.StdinPipe()
if nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -85,54 +90,13 @@ func SaveHandler(w http.ResponseWriter, r *http.Request) {
data["fileName"] = fileName
// Step3. write file
filePath := filepath.Clean(conf.Wide.Playground + "/" + fileName)
filePath := filepath.Clean(conf.Wide.Data + "/playground/" + fileName)
fout, err := os.Create(filePath)
fout.WriteString(code)
if err := fout.Close(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
}
// ShortURLHandler handles request of short URL.
func ShortURLHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
session, _ := session.HTTPSession.Get(r, "wide-session")
if session.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
url := args["url"].(string)
resp, _ := http.Post("http://dwz.cn/create.php", "application/x-www-form-urlencoded",
strings.NewReader("url="+url))
var response map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
logger.Error(err)
result.Succ = false
return
}
shortURL := url
if 0 == response["status"].(float64) {
shortURL = response["tinyurl"].(string)
}
result.Data = shortURL
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -26,43 +26,40 @@ import (
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/session"
"github.com/88250/wide/util"
"github.com/gorilla/websocket"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
// IndexHandler handles request of Playground index.
func IndexHandler(w http.ResponseWriter, r *http.Request) {
// create a HTTP session
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
httpSession.Values["id"] = strconv.Itoa(rand.Int())
httpSession.Values["username"] = "playground"
httpSession.Values["uid"] = "playground"
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
locale := conf.Wide.Locale
// try to load file
code := conf.HelloWorld
fileName := "8b7cc38b4c12e6fde5c4d15a4f2f32e5.go" // MD5 of HelloWorld.go
fileName := "6c5595ec6fbadf4cfce3edbfcfd8c6d0.go" // MD5 of HelloWorld.go
if strings.HasSuffix(r.URL.Path, ".go") {
fileNameArg := r.URL.Path[len("/playground/"):]
filePath := filepath.Clean(conf.Wide.Playground + "/" + fileNameArg)
filePath := filepath.Clean(conf.Wide.Data + "/playground/" + fileNameArg)
bytes, err := ioutil.ReadFile(filePath)
if nil != err {
@ -92,15 +89,14 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
"code": template.HTML(code), "ver": conf.WideVersion, "year": time.Now().Year(),
"embed": embed, "disqus": disqus, "fileName": fileName}
wideSessions := session.WideSessions.GetByUsername(username)
wideSessions := session.WideSessions.GetByUserId(uid)
logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions))
logger.Debugf("User [%s] has [%d] sessions", uid, len(wideSessions))
t, err := template.ParseFiles("views/playground/index.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -15,238 +15,16 @@
package playground
import (
"bufio"
"encoding/json"
"math/rand"
"github.com/88250/wide/session"
"net/http"
"os/exec"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/output"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
)
const (
outputBufMax = 128 // 128 string(rune)
outputTimeout = 100 // 100ms
)
type outputBuf struct {
content string
millisecond int64
}
// RunHandler handles request of executing a binary file.
func RunHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
}
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
}
filePath := args["executable"].(string)
cmd := exec.Command(filePath)
if conf.Docker {
output.SetNamespace(cmd)
}
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
}
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
}
outReader := bufio.NewReader(stdout)
errReader := bufio.NewReader(stderr)
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
}
wsChannel := session.PlaygroundWS[sid]
channelRet := map[string]interface{}{}
if !result.Succ {
if nil != wsChannel {
channelRet["cmd"] = "run-done"
channelRet["output"] = ""
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
return
}
channelRet["pid"] = cmd.Process.Pid
// add the process to user's process set
output.Processes.Add(wSession, cmd.Process)
go func(runningId int) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [id=%d, file=%s]", wSession.Username, sid, runningId, filePath)
// push once for front-end to get the 'run' state and pid
if nil != wsChannel {
channelRet["cmd"] = "run"
channelRet["output"] = ""
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
go func() {
defer util.Recover()
buf := outputBuf{}
for {
wsChannel := session.PlaygroundWS[sid]
if nil == wsChannel {
break
}
r, _, err := outReader.ReadRune()
if nil != err {
// remove the exited process from user process set
output.Processes.Remove(wSession, cmd.Process)
logger.Debugf("User [%s, %s] 's running [id=%d, file=%s] has done [stdout %v], ", wSession.Username, sid, runningId, filePath, err)
channelRet["cmd"] = "run-done"
channelRet["output"] = buf.content
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
break
}
oneRuneStr := string(r)
buf.content += oneRuneStr
now := time.Now().UnixNano() / int64(time.Millisecond)
if 0 == buf.millisecond {
buf.millisecond = now
}
if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" {
channelRet["cmd"] = "run"
channelRet["output"] = buf.content
buf = outputBuf{} // a new buffer
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
}
}()
buf := outputBuf{}
for {
r, _, err := errReader.ReadRune()
wsChannel := session.PlaygroundWS[sid]
if nil != err || nil == wsChannel {
break
}
oneRuneStr := string(r)
buf.content += oneRuneStr
now := time.Now().UnixNano() / int64(time.Millisecond)
if 0 == buf.millisecond {
buf.millisecond = now
}
if now-outputTimeout >= buf.millisecond || len(buf.content) > outputBufMax || oneRuneStr == "\n" {
channelRet["cmd"] = "run"
channelRet["output"] = buf.content
buf = outputBuf{} // a new buffer
err = wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
break
}
wsChannel.Refresh()
}
}
}(rand.Int())
session.RunHandler(w, r, session.PlaygroundWS)
}
// StopHandler handles request of stoping a running process.
// StopHandler handles request of stopping a running process.
func StopHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
sid := args["sid"].(string)
pid := int(args["pid"].(float64))
wSession := session.WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
return
}
output.Processes.Kill(wSession, pid)
session.StopHandler(w, r)
}

View File

@ -1,146 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package git
import (
"bufio"
"encoding/json"
"io"
"io/ioutil"
"math/rand"
"net/http"
"os"
"os/exec"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
// Clone handles request of git clone.
func CloneHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
sid := args["sid"].(string)
path := args["path"].(string)
repository := args["repository"].(string)
cmd := exec.Command("git", "clone", repository)
cmd.Dir = path
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Succ = false
return
}
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Succ = false
return
}
if !result.Succ {
return
}
channelRet := map[string]interface{}{}
if nil != session.OutputWS[sid] {
// display "START [git clone]" in front-end browser
channelRet["output"] = "<span class='start-get'>" + i18n.Get(locale, "start-git_clone").(string) + "</span>\n"
channelRet["cmd"] = "start-git_clone"
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
return
}
wsChannel.Refresh()
}
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
return
}
go func(runningId int) {
defer util.Recover()
defer cmd.Wait()
logger.Debugf("User [%s, %s] is running [git clone] [runningId=%d]", username, sid, runningId)
channelRet := map[string]interface{}{}
channelRet["cmd"] = "git clone"
// read all
buf, err := ioutil.ReadAll(reader)
if nil != err {
logger.Warn(err)
// TODO: handle clone error
}
logger.Debugf("User [%s, %s] 's running [git clone] [runningId=%d] has done: %s", username, sid, runningId, string(buf))
channelRet["output"] = "<span class='get-succ'>" + i18n.Get(locale, "git_clone-done").(string) + "</span>\n"
if nil != session.OutputWS[sid] {
wsChannel := session.OutputWS[sid]
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Warn(err)
}
wsChannel.Refresh()
}
}(rand.Int())
}

185
session/oauth.go Normal file
View File

@ -0,0 +1,185 @@
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package session
import (
"fmt"
"html/template"
"math/rand"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/util"
)
var states = map[string]string{}
// LoginRedirectHandler redirects to HacPai auth page.
func LoginRedirectHandler(w http.ResponseWriter, r *http.Request) {
loginAuthURL := conf.Wide.OAuthLoginURL + "?response_type=code&redirect_uri=" + conf.Wide.Server + "/login/callback"
// надо будет добавить ttlcache для state и проверять для предотвращения атак CSRF
state := gulu.Rand.String(16)
states[state] = state
path := loginAuthURL + "&state=" + state + "&client_id=" + conf.Wide.OAuthClientID
http.Redirect(w, r, path, http.StatusSeeOther)
}
func LoginCallbackHandler(w http.ResponseWriter, r *http.Request) {
state := r.URL.Query().Get("state")
if _, exist := states[state]; !exist {
http.Error(w, "Get state param failed", http.StatusBadRequest)
return
}
delete(states, state)
code := r.URL.Query().Get("code")
accessToken, err := util.GetOAuthToken(
conf.Wide.OAuthAccessTokenURL,
conf.Wide.OAuthClientID,
conf.Wide.OAuthClientSecret,
code,
conf.Wide.Server+`/login/callback`)
if err != nil {
http.Error(w, fmt.Sprintf(`get access_token failed. error: %s`, err.Error()), http.StatusBadRequest)
return
}
userInfo, err := util.OpenIdUserInfo(conf.Wide.OAuthUserInfoURL, accessToken)
if err != nil {
http.Error(w, fmt.Sprintf(`get user_info failed. error: %s`, err.Error()), http.StatusBadRequest)
return
}
userId := userInfo["userId"].(string)
userName := userInfo["userName"].(string)
avatar := userInfo["avatar"].(string)
user := conf.GetUser(userId)
if nil == user {
msg := addUser(userId, userName, avatar)
if userCreated != msg {
result := gulu.Ret.NewResult()
result.Code = -1
result.Msg = msg
gulu.Ret.RetResult(w, r, result)
return
}
}
// create a HTTP session
httpSession, _ := HTTPSession.Get(r, CookieName)
httpSession.Values["uid"] = userId
httpSession.Values["id"] = strconv.Itoa(rand.Int())
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
httpSession.Save(r, w)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
// LoginHandler handles request of show login page.
func LoginHandler(w http.ResponseWriter, r *http.Request) {
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale),
"locale": conf.Wide.Locale, "ver": conf.WideVersion, "year": time.Now().Year()}
t, err := template.ParseFiles("views/login.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
t.Execute(w, model)
}
// LogoutHandler handles request of user logout (exit).
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
httpSession, _ := HTTPSession.Get(r, CookieName)
httpSession.Options.MaxAge = -1
httpSession.Save(r, w)
}
// addUser add a user with the specified user id, username and avatar.
//
// 1. create the user's workspace
// 2. generate 'Hello, 世界' demo code in the workspace (a console version and a HTTP version)
// 3. update the user customized configurations, such as style.css
// 4. serve files of the user's workspace via HTTP
//
// Note: user [playground] is a reserved mock user
func addUser(userId, userName, userAvatar string) string {
if "playground" == userId {
return userExists
}
addUserMutex.Lock()
defer addUserMutex.Unlock()
for _, user := range conf.Users {
if strings.ToLower(user.Id) == strings.ToLower(userId) {
return userExists
}
}
workspace := filepath.Join(conf.Wide.Data, "workspaces", userId)
newUser := conf.NewUser(userId, userName, userAvatar, workspace)
conf.Users = append(conf.Users, newUser)
if !newUser.Save() {
return userCreateError
}
conf.CreateWorkspaceDir(workspace)
helloWorld(workspace)
conf.UpdateCustomizedConf(userId)
logger.Infof("Created a user [%s]", userId)
return userCreated
}
// helloWorld generates the 'Hello, 世界' source code.
func helloWorld(workspace string) {
dir := workspace + conf.PathSeparator + "src" + conf.PathSeparator + "hello"
if err := os.MkdirAll(dir, 0755); nil != err {
logger.Error(err)
return
}
fout, err := os.Create(dir + conf.PathSeparator + "main.go")
if nil != err {
logger.Error(err)
return
}
fout.WriteString(conf.HelloWorld)
fout.Close()
}

313
session/processes.go Normal file
View File

@ -0,0 +1,313 @@
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package session
import (
"bytes"
"encoding/json"
"math/rand"
"net/http"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/util"
)
// Type of process set.
type procs map[string][]*os.Process
// Processse of all users.
//
// <sid, []*os.Process>
var Processes = procs{}
// Exclusive lock.
var procMutex sync.Mutex
// RunHandler handles request of executing a binary file.
func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util.WSChannel) {
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Code = -1
}
sid := args["sid"].(string)
wSession := WideSessions.Get(sid)
if nil == wSession {
result.Code = -1
}
filePath := args["executable"].(string)
randInt := rand.Int()
rid := strconv.Itoa(randInt)
var cmd *exec.Cmd
if conf.Docker {
fileName := filepath.Base(filePath)
cmd = exec.Command("docker", "run", "--rm", "--name", rid, "-v", filePath+":/"+fileName,
"--memory", "64M", "--cpus", "0.1",
conf.DockerImageGo, "/"+fileName)
} else {
cmd = exec.Command(filePath)
curDir := filepath.Dir(filePath)
cmd.Dir = curDir
}
outBuf := &bytes.Buffer{}
errBuf := &bytes.Buffer{}
cmd.Stdout = outBuf
cmd.Stderr = errBuf
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Code = -1
}
wsChannel := channel[sid]
channelRet := map[string]interface{}{}
if 0 != result.Code {
channelRet["cmd"] = "run-done"
channelRet["output"] = ""
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
return
}
done := make(chan error)
go func() { done <- cmd.Wait() }()
channelRet["pid"] = cmd.Process.Pid
Processes.Add(wSession, cmd.Process)
shouldExitBuf := false
// push once for front-end to get the 'run' state and pid
if nil != wsChannel {
channelRet["cmd"] = "run"
channelRet["output"] = ""
if nil != wsChannel {
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
}
go func() {
defer gulu.Panic.Recover(nil)
logger.Debugf("User [%s, %s] is running [id=%s, file=%s]", wSession.UserId, sid, rid, filePath)
go func() {
defer gulu.Panic.Recover(nil)
for {
if shouldExitBuf {
break
}
if 1 > outBuf.Len() {
time.Sleep(7 * time.Millisecond)
continue
}
r, _, err := outBuf.ReadRune()
if nil != err {
time.Sleep(7 * time.Millisecond)
continue
}
oneRuneStr := string(r)
oneRuneStr = strings.Replace(oneRuneStr, "<", "&lt;", -1)
oneRuneStr = strings.Replace(oneRuneStr, ">", "&gt;", -1)
channelRet["cmd"] = "run"
channelRet["output"] = oneRuneStr
wsChannel := channel[sid]
if nil != wsChannel {
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
}
}()
for {
if shouldExitBuf {
break
}
if 1 > errBuf.Len() {
time.Sleep(7 * time.Millisecond)
continue
}
r, _, err := errBuf.ReadRune()
if nil != err {
time.Sleep(7 * time.Millisecond)
continue
}
oneRuneStr := string(r)
oneRuneStr = strings.Replace(oneRuneStr, "<", "&lt;", -1)
oneRuneStr = strings.Replace(oneRuneStr, ">", "&gt;", -1)
channelRet["cmd"] = "run"
channelRet["output"] = "<span class='stderr'>" + oneRuneStr + "</span>"
wsChannel := channel[sid]
if nil != wsChannel {
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
}
}()
after := time.After(5 * time.Second)
kill := false
select {
case <-after:
if conf.Docker {
killCmd := exec.Command("docker", "rm", "-f", rid)
if err := killCmd.Run(); nil != err {
logger.Errorf("executes [docker rm -f " + rid + "] failed [" + err.Error() + "], this will cause resource leaking")
}
} else {
cmd.Process.Kill()
}
channelRet["output"] = "\n<span class='stderr'>run program timeout in 5s</span>\n"
kill = true
case <-done:
channelRet["output"] = "\n<span class='stderr'>run program complete</span>\n"
}
shouldExitBuf = true
Processes.Remove(wSession, cmd.Process)
logger.Debugf("User [%s, %s] done running [id=%s, file=%s, kill=%v]", wSession.UserId, sid, rid, filePath, kill)
if nil != wsChannel {
channelRet["cmd"] = "run-done"
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
}
// StopHandler handles request of stopping a running process.
func StopHandler(w http.ResponseWriter, r *http.Request) {
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Code = -1
return
}
sid := args["sid"].(string)
pid := int(args["pid"].(float64))
wSession := WideSessions.Get(sid)
if nil == wSession {
result.Code = -1
return
}
Processes.Kill(wSession, pid)
}
// Add adds the specified process to the user process set.
func (procs *procs) Add(wSession *WideSession, proc *os.Process) {
procMutex.Lock()
defer procMutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
userProcesses = append(userProcesses, proc)
(*procs)[sid] = userProcesses
// bind process with wide session
wSession.SetProcesses(userProcesses)
logger.Tracef("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
}
// Remove removes the specified process from the user process set.
func (procs *procs) Remove(wSession *WideSession, proc *os.Process) {
procMutex.Lock()
defer procMutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
var newProcesses []*os.Process
for i, p := range userProcesses {
if p.Pid == proc.Pid {
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...) // remove it
(*procs)[sid] = newProcesses
// bind process with wide session
wSession.SetProcesses(newProcesses)
logger.Tracef("Session [%s] has [%d] processes", sid, len((*procs)[sid]))
return
}
}
}
// Kill kills a process specified by the given pid.
func (procs *procs) Kill(wSession *WideSession, pid int) {
procMutex.Lock()
defer procMutex.Unlock()
sid := wSession.ID
userProcesses := (*procs)[sid]
for i, p := range userProcesses {
if p.Pid == pid {
if err := p.Kill(); nil != err {
logger.Errorf("Kill a process [pid=%d] of user [%s, %s] failed [error=%v]", pid, wSession.UserId, sid, err)
} else {
var newProcesses []*os.Process
newProcesses = append(userProcesses[:i], userProcesses[i+1:]...)
(*procs)[sid] = newProcesses
// bind process with wide session
wSession.SetProcesses(newProcesses)
logger.Debugf("Killed a process [pid=%d] of user [%s, %s]", pid, wSession.UserId, sid)
}
return
}
}
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -31,14 +31,15 @@ import (
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/event"
"github.com/b3log/wide/log"
"github.com/b3log/wide/util"
"github.com/go-fsnotify/fsnotify"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/event"
"github.com/88250/wide/util"
"github.com/fsnotify/fsnotify"
"github.com/gorilla/sessions"
"github.com/gorilla/websocket"
)
@ -46,10 +47,12 @@ import (
const (
sessionStateActive = iota
sessionStateClosed // (not used so far)
CookieName = "wide-sess"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
var (
// SessionWS holds all session channels. <sid, *util.WSChannel>
@ -74,7 +77,7 @@ var HTTPSession = sessions.NewCookieStore([]byte("BEYOND"))
// WideSession represents a session associated with a browser tab.
type WideSession struct {
ID string // id
Username string // username
UserId string // user id
HTTPSession *sessions.Session // HTTP session related
Processes []*os.Process // process set
EventQueue *event.UserEventQueue // event queue
@ -102,7 +105,7 @@ var mutex sync.Mutex
// Invalid sessions: sessions that not used within 30 minutes, refers to WideSession.Updated field.
func FixedTimeRelease() {
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for _ = range time.Tick(time.Hour) {
hour, _ := time.ParseDuration("-30m")
@ -110,7 +113,7 @@ func FixedTimeRelease() {
for _, s := range WideSessions {
if s.Updated.Before(threshold) {
logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.Username)
logger.Debugf("Removes a invalid session [%s], user [%s]", s.ID, s.UserId)
WideSessions.Remove(s.ID)
}
@ -121,7 +124,7 @@ func FixedTimeRelease() {
// Online user statistic report.
type userReport struct {
username string
userId string
sessionCnt int
processCnt int
updated time.Time
@ -129,14 +132,14 @@ type userReport struct {
// report returns a online user statistics in pretty format.
func (u *userReport) report() string {
return "[" + u.username + "] has [" + strconv.Itoa(u.sessionCnt) + "] sessions and [" + strconv.Itoa(u.processCnt) +
return "[" + u.userId + "] has [" + strconv.Itoa(u.sessionCnt) + "] sessions and [" + strconv.Itoa(u.processCnt) +
"] running processes, latest activity [" + u.updated.Format("2006-01-02 15:04:05") + "]"
}
// FixedTimeReport reports the Wide sessions status periodically (10 minutes).
func FixedTimeReport() {
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for _ = range time.Tick(10 * time.Minute) {
users := userReports{}
@ -146,7 +149,7 @@ func FixedTimeReport() {
processCnt := len(s.Processes)
processSum += processCnt
if report, exists := contains(users, s.Username); exists {
if report, exists := contains(users, s.UserId); exists {
if s.Updated.After(report.updated) {
report.updated = s.Updated
}
@ -154,7 +157,7 @@ func FixedTimeReport() {
report.sessionCnt++
report.processCnt += processCnt
} else {
users = append(users, &userReport{username: s.Username, sessionCnt: 1, processCnt: processCnt, updated: s.Updated})
users = append(users, &userReport{userId: s.UserId, sessionCnt: 1, processCnt: processCnt, updated: s.Updated})
}
}
@ -173,9 +176,9 @@ func FixedTimeReport() {
}()
}
func contains(reports []*userReport, username string) (*userReport, bool) {
func contains(reports []*userReport, userId string) (*userReport, bool) {
for _, ur := range reports {
if username == ur.username {
if userId == ur.userId {
return ur, true
}
}
@ -219,7 +222,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
wSession := WideSessions.Get(sid)
if nil == wSession {
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession, _ := HTTPSession.Get(r, CookieName)
if httpSession.IsNew {
return
@ -230,7 +233,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
wSession = WideSessions.new(httpSession, sid)
logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.Username)
logger.Tracef("Created a wide session [%s] for websocket reconnecting, user [%s]", sid, wSession.UserId)
}
logger.Tracef("Open a new [Session Channel] with session [%s], %d", sid, len(SessionWS))
@ -262,7 +265,7 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
for {
if err := wsChan.ReadJSON(&input); err != nil {
logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.Username)
logger.Tracef("[Session Channel] of session [%s] disconnected, releases all resources with it, user [%s]", sid, wSession.UserId)
return
}
@ -281,8 +284,8 @@ func WSHandler(w http.ResponseWriter, r *http.Request) {
// SaveContentHandler handles request of session content string.
func SaveContentHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
args := struct {
Sid string
@ -291,14 +294,14 @@ func SaveContentHandler(w http.ResponseWriter, r *http.Request) {
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
wSession := WideSessions.Get(args.Sid)
if nil == wSession {
result.Succ = false
result.Code = -1
return
}
@ -306,7 +309,7 @@ func SaveContentHandler(w http.ResponseWriter, r *http.Request) {
wSession.Content = args.LatestSessionContent
for _, user := range conf.Users {
if user.Name == wSession.Username {
if user.Id == wSession.UserId {
// update the variable in-memory, session.FixedTimeSave() function will persist it periodically
user.LatestSessionContent = wSession.Content
@ -375,9 +378,9 @@ func (sessions *wSessions) Remove(sid string) {
// kill processes
for _, p := range s.Processes {
if err := p.Kill(); nil != err {
logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
logger.Errorf("Can't kill process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId)
} else {
logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.Username)
logger.Debugf("Killed a process [%d] of session [%s], user [%s]", p.Pid, sid, s.UserId)
}
}
@ -409,12 +412,12 @@ func (sessions *wSessions) Remove(sid string) {
cnt := 0 // count wide sessions associated with HTTP session
for _, ses := range *sessions {
if ses.Username == s.Username {
if ses.UserId == s.UserId {
cnt++
}
}
logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.Username, cnt)
logger.Debugf("Removed a session [%s] of user [%s], it has [%d] sessions currently", sid, s.UserId, cnt)
return
}
@ -422,14 +425,14 @@ func (sessions *wSessions) Remove(sid string) {
}
// GetByUsername gets wide sessions.
func (sessions *wSessions) GetByUsername(username string) []*WideSession {
func (sessions *wSessions) GetByUserId(userId string) []*WideSession {
mutex.Lock()
defer mutex.Unlock()
ret := []*WideSession{}
for _, s := range *sessions {
if s.Username == username {
if s.UserId == userId {
ret = append(ret, s)
}
}
@ -442,12 +445,12 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
mutex.Lock()
defer mutex.Unlock()
username := httpSession.Values["username"].(string)
uid := httpSession.Values["uid"].(string)
now := time.Now()
ret := &WideSession{
ID: sid,
Username: username,
UserId: uid,
HTTPSession: httpSession,
EventQueue: nil,
State: sessionStateActive,
@ -458,7 +461,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
*sessions = append(*sessions, ret)
if "playground" == username {
if "playground" == uid {
return ret
}
@ -474,12 +477,12 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
}
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for {
ch := SessionWS[sid]
if nil == ch {
return // release this gorutine
return // release this goroutine
}
select {
@ -489,7 +492,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
ch = SessionWS[sid]
if nil == ch {
return // release this gorutine
return // release this goroutine
}
logger.Trace(event)
@ -497,7 +500,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
if event.Op&fsnotify.Create == fsnotify.Create {
fileType := "f"
if util.File.IsDir(path) {
if gulu.File.IsDir(path) {
fileType = "d"
if err = watcher.Add(path); nil != err {
@ -505,17 +508,13 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
}
}
cmd := map[string]interface{}{"path": path, "dir": dir,
"cmd": "create-file", "type": fileType}
cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "create-file", "type": fileType}
ch.WriteJSON(&cmd)
} else if event.Op&fsnotify.Remove == fsnotify.Remove {
cmd := map[string]interface{}{"path": path, "dir": dir,
"cmd": "remove-file", "type": ""}
cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "remove-file", "type": ""}
ch.WriteJSON(&cmd)
} else if event.Op&fsnotify.Rename == fsnotify.Rename {
cmd := map[string]interface{}{"path": path, "dir": dir,
"cmd": "rename-file", "type": ""}
cmd := map[string]interface{}{"path": path, "dir": dir, "cmd": "rename-file", "type": ""}
ch.WriteJSON(&cmd)
}
case err := <-watcher.Errors:
@ -527,12 +526,12 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
}()
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
workspaces := filepath.SplitList(conf.GetUserWorkspace(username))
workspaces := filepath.SplitList(conf.GetUserWorkspace(uid))
for _, workspace := range workspaces {
filepath.Walk(filepath.Join(workspace, "src"), func(dirPath string, f os.FileInfo, err error) error {
if ".git" == f.Name() { // XXX: discard other unconcered dirs
if strings.HasPrefix(f.Name(), ".") || "node_modules" == f.Name() || "vendor" == f.Name() {
return filepath.SkipDir
}

View File

@ -1,10 +1,10 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
// Copyright (c) 2014-present, b3log.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@ -15,33 +15,26 @@
package session
import (
"crypto/md5"
"encoding/hex"
"encoding/json"
"math/rand"
"net/http"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"text/template"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/util"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
)
const (
// TODO: i18n
userExists = "user exists"
emailExists = "email exists"
userCreated = "user created"
userCreateError = "user create error"
notAllowRegister = "not allow register"
)
// Exclusive lock for adding user.
@ -49,57 +42,70 @@ var addUserMutex sync.Mutex
// PreferenceHandler handles request of preference page.
func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession, _ := HTTPSession.Get(r, CookieName)
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
http.Redirect(w, r, "/login", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
user := conf.GetUser(username)
uid := httpSession.Values["uid"].(string)
user := conf.GetUser(uid)
if "GET" == r.Method {
tmpLinux := user.GoBuildArgsForLinux
tmpWindows := user.GoBuildArgsForWindows
tmpDarwin := user.GoBuildArgsForDarwin
user.GoBuildArgsForLinux = strings.Replace(user.GoBuildArgsForLinux, `"`, `&quot;`, -1)
user.GoBuildArgsForWindows = strings.Replace(user.GoBuildArgsForWindows, `"`, `&quot;`, -1)
user.GoBuildArgsForDarwin = strings.Replace(user.GoBuildArgsForDarwin, `"`, `&quot;`, -1)
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(user.Locale), "user": user,
"ver": conf.WideVersion, "goos": runtime.GOOS, "goarch": runtime.GOARCH, "gover": runtime.Version(),
"locales": i18n.GetLocalesNames(), "gofmts": util.Go.GetGoFormats(),
"locales": i18n.GetLocalesNames(), "gofmts": gulu.Go.GetGoFormats(),
"themes": conf.GetThemes(), "editorThemes": conf.GetEditorThemes()}
t, err := template.ParseFiles("views/preference.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
http.Error(w, err.Error(), http.StatusInternalServerError)
user.GoBuildArgsForLinux = tmpLinux
user.GoBuildArgsForWindows = tmpWindows
user.GoBuildArgsForDarwin = tmpDarwin
return
}
t.Execute(w, model)
user.GoBuildArgsForLinux = tmpLinux
user.GoBuildArgsForWindows = tmpWindows
user.GoBuildArgsForDarwin = tmpDarwin
return
}
// non-GET request as save request
result := util.NewResult()
defer util.RetResult(w, r, result)
result := gulu.Ret.NewResult()
defer gulu.Ret.RetResult(w, r, result)
args := struct {
FontFamily string
FontSize string
GoFmt string
GoBuildArgsForLinux string
GoBuildArgsForWindows string
GoBuildArgsForDarwin string
Keymap string
Workspace string
Username string
Password string
Email string
Locale string
Theme string
EditorFontFamily string
@ -111,7 +117,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -119,17 +125,12 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
user.FontFamily = args.FontFamily
user.FontSize = args.FontSize
user.GoFormat = args.GoFmt
user.GoBuildArgsForLinux = args.GoBuildArgsForLinux
user.GoBuildArgsForWindows = args.GoBuildArgsForWindows
user.GoBuildArgsForDarwin = args.GoBuildArgsForDarwin
user.Keymap = args.Keymap
// XXX: disallow change workspace at present
// user.Workspace = args.Workspace
if user.Password != args.Password {
user.Password = conf.Salt(args.Password, user.Salt)
}
user.Email = args.Email
hash := md5.New()
hash.Write([]byte(user.Email))
user.Gravatar = hex.EncodeToString(hash.Sum(nil))
user.Locale = args.Locale
user.Theme = args.Theme
@ -139,147 +140,17 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
user.Editor.Theme = args.EditorTheme
user.Editor.TabSize = args.EditorTabSize
conf.UpdateCustomizedConf(username)
conf.UpdateCustomizedConf(uid)
now := time.Now().UnixNano()
user.Lived = now
user.Updated = now
result.Succ = user.Save()
}
// LoginHandler handles request of user login.
func LoginHandler(w http.ResponseWriter, r *http.Request) {
if "GET" == r.Method {
// show the login page
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale),
"locale": conf.Wide.Locale, "ver": conf.WideVersion, "year": time.Now().Year()}
t, err := template.ParseFiles("views/login.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
if user.Save() {
result.Code = 0
} else {
result.Code = -1
}
t.Execute(w, model)
return
}
// non-GET request as login request
result := util.NewResult()
defer util.RetResult(w, r, result)
args := struct {
Username string
Password string
}{}
args.Username = r.FormValue("username")
args.Password = r.FormValue("password")
result.Succ = false
for _, user := range conf.Users {
if user.Name == args.Username && user.Password == conf.Salt(args.Password, user.Salt) {
result.Succ = true
break
}
}
if !result.Succ {
return
}
// create a HTTP session
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession.Values["username"] = args.Username
httpSession.Values["id"] = strconv.Itoa(rand.Int())
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
logger.Debugf("Created a HTTP session [%s] for user [%s]", httpSession.Values["id"].(string), args.Username)
}
// LogoutHandler handles request of user logout (exit).
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession.Options.MaxAge = -1
httpSession.Save(r, w)
}
// SignUpUserHandler handles request of registering user.
func SignUpUserHandler(w http.ResponseWriter, r *http.Request) {
if "GET" == r.Method {
// show the user sign up page
firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name)
dir := filepath.Dir(firstUserWorkspace)
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(conf.Wide.Locale),
"locale": conf.Wide.Locale, "ver": conf.WideVersion, "dir": dir,
"pathSeparator": conf.PathSeparator, "year": time.Now().Year()}
t, err := template.ParseFiles("views/sign_up.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
}
t.Execute(w, model)
return
}
// non-GET request as add user request
result := util.NewResult()
defer util.RetResult(w, r, result)
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
logger.Error(err)
result.Succ = false
return
}
username := args["username"].(string)
password := args["password"].(string)
email := args["email"].(string)
msg := addUser(username, password, email)
if userCreated != msg {
result.Succ = false
result.Msg = msg
return
}
// create a HTTP session
httpSession, _ := HTTPSession.Get(r, "wide-session")
httpSession.Values["username"] = username
httpSession.Values["id"] = strconv.Itoa(rand.Int())
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
}
// FixedTimeSave saves online users' configurations periodically (1 minute).
@ -287,25 +158,19 @@ func SignUpUserHandler(w http.ResponseWriter, r *http.Request) {
// Main goal of this function is to save user session content, for restoring session content while user open Wide next time.
func FixedTimeSave() {
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for _ = range time.Tick(time.Minute) {
users := getOnlineUsers()
for _, u := range users {
if u.Save() {
logger.Tracef("Saved online user [%s]'s configurations", u.Name)
}
}
SaveOnlineUsers()
}
}()
}
// CanAccess determines whether the user specified by the given username can access the specified path.
func CanAccess(username, path string) bool {
// CanAccess determines whether the user specified by the given user id can access the specified path.
func CanAccess(userId, path string) bool {
path = filepath.FromSlash(path)
userWorkspace := conf.GetUserWorkspace(username)
userWorkspace := conf.GetUserWorkspace(userId)
workspaces := filepath.SplitList(userWorkspace)
for _, workspace := range workspaces {
@ -317,23 +182,33 @@ func CanAccess(username, path string) bool {
return false
}
// SaveOnlineUsers saves online users' configurations at once.
func SaveOnlineUsers() {
users := getOnlineUsers()
for _, u := range users {
if u.Save() {
logger.Tracef("Saved online user [%s]'s configurations", u.Name)
}
}
}
func getOnlineUsers() []*conf.User {
ret := []*conf.User{}
usernames := map[string]string{} // distinct username
uids := map[string]string{} // distinct uid
for _, s := range WideSessions {
usernames[s.Username] = s.Username
uids[s.UserId] = s.UserId
}
for _, username := range usernames {
u := conf.GetUser(username)
for _, uid := range uids {
u := conf.GetUser(uid)
if "playground" == username { // user [playground] is a reserved mock user
if "playground" == uid { // user [playground] is a reserved mock user
continue
}
if nil == u {
logger.Warnf("Not found user [%s]", username)
logger.Warnf("Not found user [%s]", uid)
continue
}
@ -343,137 +218,3 @@ func getOnlineUsers() []*conf.User {
return ret
}
// addUser add a user with the specified username, password and email.
//
// 1. create the user's workspace
// 2. generate 'Hello, 世界' demo code in the workspace (a console version and a HTTP version)
// 3. update the user customized configurations, such as style.css
// 4. serve files of the user's workspace via HTTP
//
// Note: user [playground] is a reserved mock user
func addUser(username, password, email string) string {
if !conf.Wide.AllowRegister {
return notAllowRegister
}
if "playground" == username {
return userExists
}
addUserMutex.Lock()
defer addUserMutex.Unlock()
for _, user := range conf.Users {
if strings.ToLower(user.Name) == strings.ToLower(username) {
return userExists
}
if strings.ToLower(user.Email) == strings.ToLower(email) {
return emailExists
}
}
firstUserWorkspace := conf.GetUserWorkspace(conf.Users[0].Name)
dir := filepath.Dir(firstUserWorkspace)
workspace := filepath.Join(dir, username)
newUser := conf.NewUser(username, password, email, workspace)
conf.Users = append(conf.Users, newUser)
if !newUser.Save() {
return userCreateError
}
conf.CreateWorkspaceDir(workspace)
helloWorld(workspace)
conf.UpdateCustomizedConf(username)
http.Handle("/workspace/"+username+"/",
http.StripPrefix("/workspace/"+username+"/", http.FileServer(http.Dir(newUser.GetWorkspace()))))
logger.Infof("Created a user [%s]", username)
return userCreated
}
// helloWorld generates the 'Hello, 世界' source code.
// 1. src/hello/main.go
// 2. src/web/main.go
func helloWorld(workspace string) {
consoleHello(workspace)
webHello(workspace)
}
func consoleHello(workspace string) {
dir := workspace + conf.PathSeparator + "src" + conf.PathSeparator + "hello"
if err := os.MkdirAll(dir, 0755); nil != err {
logger.Error(err)
return
}
fout, err := os.Create(dir + conf.PathSeparator + "main.go")
if nil != err {
logger.Error(err)
return
}
fout.WriteString(conf.HelloWorld)
fout.Close()
}
func webHello(workspace string) {
dir := workspace + conf.PathSeparator + "src" + conf.PathSeparator + "web"
if err := os.MkdirAll(dir, 0755); nil != err {
logger.Error(err)
return
}
fout, err := os.Create(dir + conf.PathSeparator + "main.go")
if nil != err {
logger.Error(err)
return
}
code := `package main
import (
"fmt"
"math/rand"
"net/http"
"strconv"
"time"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, 世界"))
})
port := getPort()
// you may need to change the address
fmt.Println("Open http://wide.b3log.org:" + port + " in your browser to see the result")
if err := http.ListenAndServe(":"+port, nil); nil != err {
fmt.Println(err)
}
}
func getPort() string {
rand.Seed(time.Now().UnixNano())
return strconv.Itoa(7000 + rand.Intn(8000-7000))
}
`
fout.WriteString(code)
fout.Close()
}

View File

@ -1,189 +0,0 @@
// Copyright (c) 2014-2016, b3log.org & hacpai.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package shell include shell related mainipulations.
package shell
import (
"html/template"
"net/http"
"os"
"os/exec"
"runtime"
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/log"
"github.com/b3log/wide/session"
"github.com/b3log/wide/util"
"github.com/gorilla/websocket"
)
// Shell channel.
//
// <sid, *util.WSChannel>>
var ShellWS = map[string]*util.WSChannel{}
// Logger.
var logger = log.NewLogger(os.Stdout)
// IndexHandler handles request of Shell index.
func IndexHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Redirect(w, r, conf.Wide.Context+"login", http.StatusFound)
return
}
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
if "" != conf.Wide.Context {
httpSession.Options.Path = conf.Wide.Context
}
httpSession.Save(r, w)
username := httpSession.Values["username"].(string)
locale := conf.GetUser(username).Locale
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"sid": session.WideSessions.GenId()}
wideSessions := session.WideSessions.GetByUsername(username)
logger.Tracef("User [%s] has [%d] sessions", username, len(wideSessions))
t, err := template.ParseFiles("views/shell.html")
if nil != err {
logger.Error(err)
http.Error(w, err.Error(), 500)
return
}
t.Execute(w, model)
}
// WSHandler handles request of creating Shell channel.
func WSHandler(w http.ResponseWriter, r *http.Request) {
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
sid := r.URL.Query()["sid"][0]
conn, _ := websocket.Upgrade(w, r, nil, 1024, 1024)
wsChan := util.WSChannel{Sid: sid, Conn: conn, Request: r, Time: time.Now()}
ret := map[string]interface{}{"output": "Shell initialized", "cmd": "init-shell"}
err := wsChan.WriteJSON(&ret)
if nil != err {
return
}
ShellWS[sid] = &wsChan
logger.Debugf("Open a new [Shell] with session [%s], %d", sid, len(ShellWS))
input := map[string]interface{}{}
for {
if err := wsChan.ReadJSON(&input); err != nil {
logger.Error("Shell WS ERROR: " + err.Error())
return
}
inputCmd := input["cmd"].(string)
cmds := strings.Split(inputCmd, "|")
commands := []*exec.Cmd{}
for _, cmdWithArgs := range cmds {
cmdWithArgs = strings.TrimSpace(cmdWithArgs)
cmdWithArgs := strings.Split(cmdWithArgs, " ")
args := []string{}
if len(cmdWithArgs) > 1 {
args = cmdWithArgs[1:]
}
cmd := exec.Command(cmdWithArgs[0], args...)
commands = append(commands, cmd)
}
output := ""
if !strings.Contains(inputCmd, "clear") {
output = pipeCommands(username, commands...)
}
ret = map[string]interface{}{"output": output, "cmd": "shell-output"}
if err := wsChan.WriteJSON(&ret); err != nil {
logger.Error("Shell WS ERROR: " + err.Error())
return
}
wsChan.Refresh()
}
}
func pipeCommands(username string, commands ...*exec.Cmd) string {
for i, command := range commands[:len(commands)-1] {
setCmdEnv(command, username)
stdout, err := command.StdoutPipe()
if nil != err {
return err.Error()
}
command.Start()
commands[i+1].Stdin = stdout
}
last := commands[len(commands)-1]
setCmdEnv(last, username)
out, err := last.CombinedOutput()
// release resources
for _, command := range commands[:len(commands)-1] {
command.Wait()
}
if err != nil {
return err.Error()
}
return string(out)
}
func setCmdEnv(cmd *exec.Cmd, username string) {
userWorkspace := conf.GetUserWorkspace(username)
cmd.Env = append(cmd.Env,
"TERM="+os.Getenv("TERM"),
"GOPATH="+userWorkspace,
"GOOS="+runtime.GOOS,
"GOARCH="+runtime.GOARCH,
"GOROOT="+runtime.GOROOT(),
"PATH="+os.Getenv("PATH"))
cmd.Dir = userWorkspace
}

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -18,7 +18,7 @@
* themes for base.
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @version 0.1.0.0, Dec 6, 2015
* @version 0.2.0.0, Oct 5, 2018
*/
/* start reset & function */
::-webkit-scrollbar {
@ -140,18 +140,24 @@ button {
/* start icon */
@font-face {
font-family: 'icomoon';
src:url('fonts/icomoon.eot?35cb2z');
src:url('fonts/icomoon.eot?#iefix35cb2z') format('embedded-opentype'),
url('fonts/icomoon.woff?35cb2z') format('woff'),
url('fonts/icomoon.ttf?35cb2z') format('truetype'),
url('fonts/icomoon.svg?35cb2z#icomoon') format('svg');
src: url('fonts/icomoon.eot?lqk80d');
src: url('fonts/icomoon.eot?lqk80d#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?lqk80d') format('truetype'),
url('fonts/icomoon.woff?lqk80d') format('woff'),
url('fonts/icomoon.svg?lqk80d#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
.font-ico,
[class^="ico-"] {
font-family: 'icomoon';
[class^="ico-"], [class*=" ico-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
@ -160,177 +166,140 @@ button {
line-height: 20px;
}
.ico-book:before {
content: "\e623";
.ico-qqz:before {
content: "\e900";
}
.ico-price:before {
content: "\e616";
}
.ico-start:before {
content: "\e9d7";
}
.ico-share:before {
content: "\e61f";
}
.ico-github:before {
content: "\f00a";
}
.ico-git:before {
content: "\e624";
}
.ico-tencent:before {
content: "\e622";
}
.ico-weibo:before {
content: "\e621";
}
.ico-googleplus:before {
content: "\e61a";
}
.ico-twitter:before {
content: "\e61c";
}
.ico-email:before {
content: "\e619";
}
.ico-facebook:before {
content: "\e61b";
}
.ico-moveup:before {
content: "\f148";
}
.ico-movedown:before {
content: "\f149";
}
.ico-keyboard:before {
content: "\f11c";
}
.ico-findfiles:before {
content: "\e603";
}
.ico-find:before {
content: "\e602";
}
.ico-findfiles:before {
content: "\e603";
}
.ico-editor:before {
content: "\e604";
}
.ico-tree:before {
content: "\e600";
}
.ico-build:before {
content: "\e601";
}
.ico-notification:before {
content: "\e607";
}
.ico-price:before {
content: "\e616";
}
.ico-report:before {
content: "\e605";
}
.ico-comment:before {
content: "\e620";
.ico-git:before {
content: "\e624";
}
.ico-goline:before {
content: "\e61e";
.ico-book:before {
content: "\e623";
}
.ico-info:before {
content: "\e61d";
.ico-start:before {
content: "\e9d7";
text-shadow: 0 0 rgba(0, 0, 0, 0.4);
}
.ico-signup:before {
content: "\e606";
.ico-tree:before {
content: "\e600";
}
.ico-signout:before {
content: "\e618";
.ico-build:before {
content: "\e601";
}
.ico-redo:before {
content: "\e615";
}
.ico-undo:before {
content: "\e60e";
}
.ico-about:before {
content: "\e60d";
}
.ico-import:before {
content: "\f0ee";
}
.ico-export:before {
content: "\f0ed";
}
.ico-refresh:before {
content: "\f021";
.ico-import:before {
content: "\f0ee";
}
.ico-remove:before {
content: "\e60b";
.ico-keyboard:before {
content: "\f11c";
}
.ico-save:before {
content: "\f0c7";
.ico-moveup:before {
content: "\f148";
}
.ico-movedown:before {
content: "\f149";
}
.ico-weibo:before {
content: "\e621";
}
.ico-uniE608:before {
content: "\e608";
}
.ico-max:before {
content: "\e609";
}
.ico-format:before {
content: "\e612";
.ico-remove:before {
content: "\e60b";
}
.ico-buildrun:before {
content: "\e60c";
}
.ico-about:before {
content: "\e60d";
}
.ico-undo:before {
content: "\e60e";
}
.ico-stop:before {
content: "\e60f";
}
.ico-close:before {
content: "\e611";
text-shadow: 0 0 rgba(0, 0, 0, 0.4);
}
.ico-format:before {
content: "\e612";
}
.ico-restore:before {
content: "\e613";
}
.toolbars .ico-restore:before {
content: "\e60a";
}
.ico-min:before {
content: "\e614";
position: absolute;
right: 5px;
}
.ico-close:before {
content: "\e611";
.ico-redo:before {
content: "\e615";
}
.ico-uniE617:before {
content: "\e617";
}
.ico-signout:before {
content: "\e618";
}
.ico-email:before {
content: "\e619";
}
.ico-googleplus:before {
content: "\e61a";
}
.ico-facebook:before {
content: "\e61b";
}
.ico-twitter:before {
content: "\e61c";
}
.ico-info:before {
content: "\e61d";
}
.ico-goline:before {
content: "\e61e";
}
.ico-share:before {
content: "\e61f";
}
.ico-comment:before {
content: "\e620";
}
.ico-github:before {
content: "\f00a";
}
.ico-refresh:before {
content: "\f021";
}
.ico-save:before {
content: "\f0c7";
}
/* end ico */

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -17,7 +17,7 @@
/**
* dialig style
*
* @author <a href="mailto:LLY219@gmail.com">Liyuan Li</a>
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @version 0.0.0.6, Jun 3, 2012
*/

BIN
static/css/fonts/icomoon.eot Normal file → Executable file

Binary file not shown.

94
static/css/fonts/icomoon.svg Normal file → Executable file
View File

@ -6,52 +6,50 @@
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe600;" d="M0 886.858c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM292.572 960h877.714v-146.286h-877.714zM292.572 594.286c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM585.142 667.428h585.144v-146.286h-585.144zM292.572 9.142c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM585.142 82.286h585.144v-146.286h-585.144zM585.142 301.714c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM877.714 374.858h292.572v-146.286h-292.572z" horiz-adv-x="1170" />
<glyph unicode="&#xe601;" d="M1009.996 131.024l-301.544 301.544c-18.668 18.668-49.214 18.668-67.882 0l-22.626-22.626-184 184 302.056 302.058h-320l-142.058-142.058-14.060 14.058h-67.882v-67.882l14.058-14.058-206.058-206.060 160-160 206.058 206.058 184-184-22.626-22.626c-18.668-18.668-18.668-49.214 0-67.882l301.544-301.544c18.668-18.668 49.214-18.668 67.882 0l113.136 113.136c18.67 18.666 18.67 49.214 0.002 67.882z" />
<glyph unicode="&#xe602;" d="M632.576 661.376c46.080-10.666 82.752-49.92 93.418-92.8 3.648-20.054 21.12-35.2 42.048-35.2 23.466 0 42.666 19.2 42.666 42.666 0 30.72-25.814 77.44-61.014 112.426-34.56 34.134-75.734 58.24-109.654 58.24-23.466 0-42.666-19.84-42.666-43.306 0.002-20.884 15.148-38.4 35.202-42.026zM46.122 102.016c-33.066-33.046-33.066-86.826 0-119.872 33.066-33.088 86.826-33.088 119.68 0l245.098 244.906c57.194-35.67 124.586-56.342 197.12-56.342 206.294 0 373.334 167.040 373.334 373.334s-167.040 373.334-373.334 373.334-373.334-167.040-373.334-373.334c0-72.32 20.714-139.946 56.342-197.12l-244.906-244.906zM341.376 544.042c0 147.2 119.488 266.666 266.666 266.666 147.2 0 266.666-119.466 266.666-266.666s-119.466-266.666-266.666-266.666c-147.178 0.022-266.666 119.488-266.666 266.666z" />
<glyph unicode="&#xe603;" d="M0 21.334c0-47.146 38.186-85.334 85.334-85.334h277.334c47.146 0 85.334 38.186 85.334 85.334l-0.002 341.332h128v-341.334c0-47.146 38.186-85.334 85.334-85.334h277.334c47.146 0 85.334 38.186 85.334 85.334l-0.002 554.668c0 47.146-38.186 85.334-85.334 85.334h-42.666v192h10.666c29.44 0 53.334 23.892 53.334 53.332s-23.894 53.334-53.334 53.334h-277.334c-29.438 0-53.332-23.894-53.332-53.334s23.894-53.332 53.334-53.332h10.666v-192h-256v192h10.666c29.44 0 53.334 23.892 53.334 53.332s-23.894 53.334-53.334 53.334h-277.334c-29.438 0-53.332-23.894-53.332-53.334s23.894-53.332 53.334-53.332h10.666v-192h-42.666c-47.146 0-85.334-38.186-85.334-85.334v-554.666zM554.666 469.334h-85.334c-23.466 0-42.666 19.2-42.666 42.666s19.2 42.666 42.666 42.666h85.334c23.466 0 42.666-19.2 42.666-42.666s-19.198-42.666-42.666-42.666z" />
<glyph unicode="&#xe604;" d="M118.634 528.918c-20.886-20.886-30.934-50.774-36.032-81.066l-81.322-384.428c-11.52-68.288 56.96-136.982 126.102-126.314l384.874 80.234c28.16 5.098 58.666 13.866 79.574 34.774l407.68 408.128c32.64 32.832 32.64 85.952 0 118.806l-356.054 356.48c-32.854 32.64-85.974 32.64-118.634 0l-406.188-406.614zM509.888 118.678l-199.254-38.208-165.12 165.334 37.952 199.488 296.768 297.174c16.426 16.448 42.88 16.448 59.306 0 16.448-16.426 16.448-43.094 0-59.52l-274.368-274.774c-20.48-20.48-20.48-53.782 0-74.218 20.48-20.502 53.546-20.502 74.026 0l274.582 274.774c16.448 16.426 42.88 16.426 59.306 0 16.448-16.448 16.448-42.902 0-59.306l-274.368-274.774c-20.48-20.48-20.48-53.782 0-74.24 20.48-20.48 53.568-20.48 74.026 0l274.582 274.774c16.234 16.234 42.902 16.234 59.328 0 16.426-16.426 16.426-43.094 0-59.498l-296.766-297.006z" />
<glyph unicode="&#xe605;" d="M622.858 778.475l-22.032 110.158h-495.715v-936.349h110.158v385.556h308.443l22.032-110.158h385.556v550.795z" />
<glyph unicode="&#xe606;" d="M697.83 473.125c76.887 0 138.908 62.486 138.908 139.372s-62.021 139.372-138.908 139.372c-76.887 0-139.372-62.486-139.372-139.372s62.486-139.372 139.372-139.372zM326.17 473.125c76.887 0 138.908 62.486 138.908 139.372s-62.021 139.372-138.908 139.372c-76.887 0-139.372-62.486-139.372-139.372s62.486-139.372 139.372-139.372zM326.17 380.209c-108.479 0-325.203-54.355-325.203-162.601v-116.144h650.404v116.144c0 108.245-216.724 162.601-325.203 162.601zM697.83 380.209c-13.472 0-28.571-0.929-44.831-2.555 53.89-38.792 91.289-91.057 91.289-160.047v-116.144h278.744v116.144c0 108.245-216.724 162.601-325.203 162.601z" />
<glyph unicode="&#xe607;" d="M977.76 152.992l-400.608 685.952c-33.408 33.408-87.616 33.408-121.024 0l-400.64-685.952c-33.408-33.376-33.408-87.552 0-120.992h922.24c33.472 33.44 33.472 87.616 0.032 120.992zM479.744 592.704c0 26.528 21.504 48 48 48s48-21.472 48-48v-224c0-26.496-21.504-48-48-48s-48 21.504-48 48v224zM528.032 160.448c-26.496 0-48 21.44-48 48 0 26.496 21.504 48 48 48s48-21.504 48-48c0-26.56-21.504-48-48-48z" />
<glyph unicode="&#xe608;" d="M438.928 896.072q14.856 0 25.714-10.856t10.858-25.714v-128h168l98.856 98.856q10.856 10.856 25.714 10.856t25.714-10.856 10.856-25.714-10.856-25.714l-98.856-98.856v-482.286l98.856-98.856q10.856-10.856 10.856-25.714t-10.856-25.714-25.714-10.856-25.714 10.856l-98.856 98.856h-168v-128q0-14.856-10.856-25.714t-25.714-10.856-25.714 10.856-10.856 25.714v128q-97.714 0-165.714 38.286l-119.43-118.856q-10.856-10.856-25.714-10.856t-25.714 10.856q-10.856 10.286-10.856 25.714t10.856 25.714l112.57 113.144q-2.856 2.856-7.43 8.572t-16.286 24-20.856 37.144-16.57 46.856-7.43 55.428h512v73.144h-512q0 29.144 7.714 58t18.856 49.714 22.286 37.714 18.57 24.856l8 8.572-118.286 104.57q-12 11.43-12 27.43 0 13.714 9.144 24.57 10.286 10.858 25.43 11.714t26.57-8.856l129.714-115.428q65.144 33.142 156.57 33.142v128q0 14.858 10.858 25.714t25.714 10.856zM768.072 621.786q76 0 129.428-53.428t53.428-129.428-53.428-129.428-129.428-53.428v365.714z" horiz-adv-x="952" />
<glyph unicode="&#xe609;" d="M1024 960v-384l-138.26 138.26-212-212-107.48 107.48 212 212-138.26 138.26zM245.74 821.74l212-212-107.48-107.48-212 212-138.26-138.26v384h384zM885.74 181.74l138.26 138.26v-384h-384l138.26 138.26-212 212 107.48 107.48zM457.74 286.26l-212-212 138.26-138.26h-384v384l138.26-138.26 212 212z" />
<glyph unicode="&#xe60a;" d="M64 384h384v-384l-138.26 138.26-202-202-107.48 107.48 202 202zM821.74 245.74l202-202-107.48-107.48-202 202-138.26-138.26v384h384zM960 512h-384v384l138.26-138.26 202 202 107.48-107.48-202-202zM309.74 757.74l138.26 138.26v-384h-384l138.26 138.26-202 202 107.48 107.48z" />
<glyph unicode="&#xe60b;" d="M800 832h-576c-53.020 0-96-42.98-96-96v-32h768v32c0 53.020-42.98 96-96 96zM632.32 896l14.116-101h-268.872l14.114 101h240.642zM640 960h-256c-26.4 0-50.99-21.392-54.642-47.538l-18.714-133.924c-3.654-26.146 14.956-47.538 41.356-47.538h320c26.4 0 45.010 21.392 41.358 47.538l-18.714 133.924c-3.654 26.146-28.244 47.538-54.644 47.538v0zM816 640h-608c-35.2 0-61.392-28.682-58.206-63.738l52.412-576.526c3.186-35.054 34.594-63.736 69.794-63.736h480c35.2 0 66.608 28.682 69.794 63.736l52.41 576.526c3.188 35.056-23.004 63.738-58.204 63.738zM384 64h-96l-32 448h128v-448zM576 64h-128v448h128v-448zM736 64h-96v448h128l-32-448z" />
<glyph unicode="&#xe60c;" d="M192 832l640-384-640-384z" />
<glyph unicode="&#xe60d;" d="M708.524 833.167c-54.823 39.545-123.584 59.295-206.423 59.295-63.041 0-116.185-13.935-159.382-41.708-68.567-43.534-104.989-117.434-109.41-221.702h158.852c0 30.367 8.841 59.629 26.572 87.787s47.81 42.236 90.238 42.236c43.101 0 72.844-11.436 89.084-34.26 16.289-22.92 24.41-48.242 24.41-76.015 0-24.169-12.108-46.272-26.714-66.405-8.024-11.726-18.644-22.488-31.759-32.387 0 0-86.154-55.258-124.016-99.657-21.959-25.755-23.929-64.291-25.851-119.598-0.144-3.94 1.346-12.059 15.136-12.059s111.332 0 123.584 0 14.8 9.083 14.991 13.069c0.865 20.133 3.123 30.416 6.823 42.044 6.967 21.959 25.803 41.129 47.042 57.614l43.726 30.176c39.447 30.752 70.97 55.978 84.855 75.775 23.738 32.578 40.409 72.651 40.409 120.171 0 77.601-27.437 136.126-82.166 175.623zM499.553 223.413c-54.777 1.633-99.944-36.231-101.672-95.619-1.73-59.342 41.227-98.549 96.004-100.184 57.18-1.682 101.145 34.981 102.875 94.322 1.682 59.39-40.026 99.8-97.205 101.481z" />
<glyph unicode="&#xe60e;" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
<glyph unicode="&#xe60f;" d="M128 832h768v-768h-768z" />
<glyph unicode="&#xe610;" d="M363.722 237.948l41.298 57.816-45.254 45.256-57.818-41.296c-10.722 5.994-22.204 10.774-34.266 14.192l-11.682 70.084h-64l-11.68-70.086c-12.062-3.418-23.544-8.198-34.266-14.192l-57.818 41.298-45.256-45.256 41.298-57.816c-5.994-10.72-10.774-22.206-14.192-34.266l-70.086-11.682v-64l70.086-11.682c3.418-12.060 8.198-23.544 14.192-34.266l-41.298-57.816 45.254-45.256 57.818 41.296c10.722-5.994 22.204-10.774 34.266-14.192l11.682-70.084h64l11.68 70.086c12.062 3.418 23.544 8.198 34.266 14.192l57.818-41.296 45.254 45.256-41.298 57.816c5.994 10.72 10.774 22.206 14.192 34.266l70.088 11.68v64l-70.086 11.682c-3.418 12.060-8.198 23.544-14.192 34.266zM224 96c-35.348 0-64 28.654-64 64s28.652 64 64 64 64-28.654 64-64-28.652-64-64-64zM1024 576v64l-67.382 12.25c-1.242 8.046-2.832 15.978-4.724 23.79l57.558 37.1-24.492 59.128-66.944-14.468c-4.214 6.91-8.726 13.62-13.492 20.13l39.006 56.342-45.256 45.254-56.342-39.006c-6.512 4.766-13.22 9.276-20.13 13.494l14.468 66.944-59.128 24.494-37.1-57.558c-7.812 1.892-15.744 3.482-23.79 4.724l-12.252 67.382h-64l-12.252-67.382c-8.046-1.242-15.976-2.832-23.79-4.724l-37.098 57.558-59.128-24.492 14.468-66.944c-6.91-4.216-13.62-8.728-20.13-13.494l-56.342 39.006-45.254-45.254 39.006-56.342c-4.766-6.51-9.278-13.22-13.494-20.13l-66.944 14.468-24.492-59.128 57.558-37.1c-1.892-7.812-3.482-15.742-4.724-23.79l-67.384-12.252v-64l67.382-12.25c1.242-8.046 2.832-15.978 4.724-23.79l-57.558-37.1 24.492-59.128 66.944 14.468c4.216-6.91 8.728-13.618 13.494-20.13l-39.006-56.342 45.254-45.256 56.342 39.006c6.51-4.766 13.22-9.276 20.13-13.492l-14.468-66.944 59.128-24.492 37.102 57.558c7.81-1.892 15.742-3.482 23.788-4.724l12.252-67.384h64l12.252 67.382c8.044 1.242 15.976 2.832 23.79 4.724l37.1-57.558 59.128 24.492-14.468 66.944c6.91 4.216 13.62 8.726 20.13 13.492l56.342-39.006 45.256 45.256-39.006 56.342c4.766 6.512 9.276 13.22 13.492 20.13l66.944-14.468 24.492 59.13-57.558 37.1c1.892 7.812 3.482 15.742 4.724 23.79l67.382 12.25zM672 468.8c-76.878 0-139.2 62.322-139.2 139.2s62.32 139.2 139.2 139.2 139.2-62.322 139.2-139.2c0-76.878-62.32-139.2-139.2-139.2z" />
<glyph unicode="&#xe611;" d="M734.668 299.418l-141.21 161.382 141.21 161.382c24.014 24.014 24.014 62.926 0 86.886s-62.924 23.962-86.886 0l-135.782-155.186-135.73 155.136c-24.012 24.012-62.926 24.012-86.886 0s-23.962-62.926 0-86.886l141.158-161.332-141.21-161.382c-23.962-24.014-23.962-62.822 0-86.784 24.012-24.014 62.926-24.014 86.886 0l135.782 155.086 135.73-155.086c24.014-24.014 62.924-24.014 86.886 0s24.014 62.77 0.052 86.784z" />
<glyph unicode="&#xe612;" d="M448 165.49l-205.254 237.254 58.508 58.51 146.746-114.744 274.744 242.744 58.512-58.508zM831.772 832c0.078-0.066 0.162-0.15 0.228-0.23v-767.542c-0.066-0.078-0.15-0.162-0.228-0.228h-639.544c-0.080 0.066-0.162 0.15-0.228 0.228v767.544c0.066 0.080 0.15 0.162 0.23 0.228h-128.23v-768c0-70.4 57.6-128 128-128h640c70.4 0 128 57.6 128 128v768h-128.228zM640 832v64c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-64h-128v-128h512v128h-128zM576 832h-128v64h128v-64z" />
<glyph unicode="&#xe613;" d="M128 682.666v128h896v-640h-128v-128h-896v640h128zM896 256h42.668v469.334h-725.332v-42.666h682.668v-426.668zM85.332 128h725.332v469.334h-725.332v-469.334z" />
<glyph unicode="&#xe614;" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
<glyph unicode="&#xe615;" d="M576 711.628v248.372l384-384-384-384v253.824c-446.75 10.482-427.588-303.792-313.86-509.824-280.712 303.414-221.1 789.57 313.86 775.628z" />
<glyph unicode="&#xe616;" d="M501.443 484.729c-119.819 31.143-158.353 63.078-158.353 113.223 0 57.535 53.047 97.916 142.518 97.916 93.957 0 128.794-44.867 131.961-110.848h116.654c-3.431 91.053-59.119 173.925-169.439 201.109v115.599h-158.353v-114.015c-102.402-22.433-184.746-88.413-184.746-190.553 0-121.933 101.082-182.636 248.086-217.999 132.225-31.671 158.353-77.856 158.353-127.474 0-36.159-25.601-94.219-142.518-94.219-108.736 0-151.755 48.826-157.297 110.848h-116.389c6.597-115.599 92.901-180.26 194.511-202.165v-114.544h158.353v113.487c102.667 19.794 184.746 79.178 184.746 187.65 0 149.38-128.266 200.581-248.086 231.987z" />
<glyph unicode="&#xe617;" d="M384 448h-320v128h320v128l192-192-192-192zM1024 960v-832l-384-192v192h-384v256h64v-192h320v576l256 128h-576v-256h-64v320z" />
<glyph unicode="&#xe618;" d="M768 320v128h-320v128h320v128l192-192zM704 384v-256h-320v-192l-384 192v832h704v-320h-64v256h-512l256-128v-576h256v192z" />
<glyph unicode="&#xe619;" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM256 704h512c9.138 0 18.004-1.962 26.144-5.662l-282.144-329.168-282.144 329.17c8.14 3.696 17.006 5.66 26.144 5.66zM192 256v384c0 1.34 0.056 2.672 0.14 4l187.664-218.942-185.598-185.598c-1.444 5.336-2.206 10.886-2.206 16.54zM768 192h-512c-5.654 0-11.202 0.762-16.54 2.208l182.118 182.118 90.422-105.498 90.424 105.494 182.116-182.12c-5.34-1.44-10.886-2.202-16.54-2.202zM832 256c0-5.654-0.762-11.2-2.206-16.54l-185.6 185.598 187.666 218.942c0.084-1.328 0.14-2.66 0.14-4v-384z" />
<glyph unicode="&#xe61a;" d="M437.006 141.838c0-75.068-46.39-134.392-177.758-139.176-76.984 43.786-141.49 106.952-186.908 182.866 23.69 58.496 97.692 103.046 182.316 102.114 24.022-0.252 46.41-4.114 66.744-10.7 55.908-38.866 101-63.152 112.324-107.448 2.114-8.964 3.282-18.206 3.282-27.656zM512 960c-147.94 0-281.196-62.77-374.666-163.098 36.934 20.452 80.538 32.638 126.902 32.638 67.068 0 256.438 0 256.438 0l-57.304-60.14h-67.31c47.496-27.212 72.752-83.248 72.752-145.012 0-56.692-31.416-102.38-75.78-137.058-43.28-33.802-51.492-47.966-51.492-76.734 0-24.542 51.722-61.098 75.5-78.936 82.818-62.112 99.578-101.184 99.578-178.87 0-78.726-68.936-157.104-185.866-183.742 56.348-21.338 117.426-33.048 181.248-33.048 282.77 0 512 229.23 512 512s-229.23 512-512 512zM768 576v-128h-64v128h-128v64h128v128h64v-128h128v-64h-128zM365.768 620.528c11.922-90.776-27.846-149.19-96.934-147.134-69.126 2.082-134.806 65.492-146.74 156.242-11.928 90.788 34.418 160.254 103.53 158.196 69.090-2.074 128.22-76.542 140.144-167.304zM220.886 317.932c-74.68 0-138.128-25.768-182.842-63.864-24.502 59.82-38.044 125.29-38.044 193.932 0 56.766 9.256 111.368 26.312 162.396 7.374-99.442 77.352-176.192 192.97-176.192 8.514 0 16.764 0.442 24.874 1.022-7.95-15.23-13.622-32.19-13.622-49.982 0-29.97 16.488-47.070 36.868-66.894-15.402 0-30.27-0.418-46.516-0.418z" />
<glyph unicode="&#xe61b;" d="M512 960c282.77 0 512-229.23 512-512 0-261.094-195.438-476.53-448-508.026v380.026h176l16 128h-192v64c0 35.346 28.654 64 64 64h128v128h-128c-106.040 0-192-85.96-192-192v-64h-96v-128h96v-380.026c-252.562 31.496-448 246.932-448 508.026 0 282.77 229.23 512 512 512z" />
<glyph unicode="&#xe61c;" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM806.242 598.912c0.292-6.508 0.442-13.056 0.442-19.638 0-200.622-152.708-431.964-431.958-431.964-85.736 0-165.536 25.136-232.726 68.214 11.876-1.408 23.962-2.126 36.216-2.126 71.13 0 136.59 24.276 188.55 64.994-66.434 1.22-122.5 45.122-141.824 105.432 9.274-1.768 18.784-2.722 28.566-2.722 13.846 0 27.258 1.856 39.998 5.324-69.452 13.952-121.786 75.312-121.786 148.868 0 0.64 0 1.278 0.016 1.91 20.47-11.37 43.878-18.2 68.764-18.988-40.74 27.222-67.54 73.692-67.54 126.368 0 27.822 7.488 53.904 20.556 76.324 74.878-91.854 186.748-152.292 312.924-158.628-2.588 11.118-3.93 22.702-3.93 34.604 0 83.84 67.98 151.812 151.818 151.812 43.666 0 83.124-18.436 110.818-47.94 34.58 6.808 67.074 19.442 96.412 36.84-11.336-35.454-35.41-65.206-66.752-83.994 30.71 3.668 59.968 11.832 87.194 23.904-20.35-30.448-46.090-57.186-75.758-78.594z" />
<glyph unicode="&#xe61d;" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM448 704h128v-128h-128zM640 192h-256v64h64v192h-64v64h192v-256h64z" />
<glyph unicode="&#xe61e;" d="M384 128h640v-128h-640zM384 512h640v-128h-640zM384 896h640v-128h-640zM192 960v-256h-64v192h-64v64zM128 434v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM256 256v-320h-192v64h128v64h-128v64h128v64h-128v64z" />
<glyph unicode="&#xe61f;" d="M864 256c-45.16 0-85.92-18.738-115.012-48.83l-431.004 215.502c1.314 8.252 2.016 16.706 2.016 25.328s-0.702 17.076-2.016 25.326l431.004 215.502c29.092-30.090 69.852-48.828 115.012-48.828 88.366 0 160 71.634 160 160s-71.634 160-160 160-160-71.634-160-160c0-8.622 0.704-17.076 2.016-25.326l-431.004-215.504c-29.092 30.090-69.852 48.83-115.012 48.83-88.366 0-160-71.636-160-160 0-88.368 71.634-160 160-160 45.16 0 85.92 18.738 115.012 48.828l431.004-215.502c-1.312-8.25-2.016-16.704-2.016-25.326 0-88.368 71.634-160 160-160s160 71.632 160 160c0 88.364-71.634 160-160 160z" />
<glyph unicode="&#xe620;" d="M0 343.808l321.344-206.624v149.152l-191.328 118.144 191.328 118.144v150.176l-321.344-207.68v-121.312zM389.312 64h110.592l162.048 768h-111.328l-161.312-768zM702.688 672.8v-150.176l191.264-118.144-191.264-118.112v-149.152l321.312 206.592v121.312l-321.312 207.68z" />
<glyph unicode="&#xe621;" d="M428.222 493.19c-144.194-6.656-260.742-83.888-260.742-180.224 0-96.202 116.574-168.528 260.742-161.792 144.302 6.576 261.094 96.876 261.094 193.078 0 96.12-116.79 155.594-261.094 148.938zM526.472 250.88c-44.168-56.994-131.53-84.802-216.36-38.858-40.394 21.908-38.884 64.916-38.884 64.916s-16.76 135.842 128.27 152.818c145.192 16.816 171.116-121.856 126.976-178.876zM429.19 341.854c-9.298-6.736-11.182-19.618-6.144-27.62 4.85-8.22 16.142-9.162 25.276-2.318 8.974 7.086 12.45 19.428 7.572 27.62-4.798 7.976-15.952 10.294-26.704 2.318zM360.448 323.154c-27.108-2.802-46.484-26.408-46.484-48.99 0-22.636 21.826-38.264 48.882-35.086 26.974 3.072 48.908 23.928 48.908 46.484 0.054 22.636-20.184 40.582-51.308 37.592zM868.082 960h-712.22c-86.096 0-155.89-69.794-155.89-155.89v-712.22c0-86.096 69.794-155.89 155.89-155.89h712.22c86.096 0 155.89 69.794 155.89 155.89v712.22c0 86.096-69.768 155.89-155.89 155.89zM818.202 280.386c-59.446-126.274-255.462-187.716-400.734-176.344-138.050 10.86-315.526 56.724-333.878 223.798 0 0-9.7 75.668 63.65 173.568 0 0 105.498 147.322 228.378 189.36 122.988 41.85 137.35-28.968 137.35-70.818-6.548-35.49-18.782-56.374 27.38-42.038 0 0 120.886 56.076 170.658 6.332 40.126-40.152 6.63-95.42 6.63-95.42s-16.654-18.404 17.624-25.034c34.358-6.872 142.362-56.914 82.944-183.404zM698.988 629.274c-13.15 0-23.714 10.644-23.714 23.688 0 13.286 10.562 23.956 23.714 23.956 0 0 148.212 27.404 130.48-131.852 0-0.942-0.108-1.698-0.322-2.534-1.672-11.29-11.586-19.94-23.256-19.94-13.204 0-23.956 10.562-23.956 23.74 0-0.026 23.498 106.416-82.944 82.944zM949.518 501.894h-0.216c-3.908-26.948-17.274-29.104-33.198-29.104-19.052 0-34.438 11.964-34.438 31.044 0 16.52 6.846 33.308 6.846 33.308 2.020 6.952 18.136 50.176-10.644 114.796-52.708 88.522-158.856 89.816-171.384 84.776-12.638-4.958-31.286-7.436-31.286-7.436-19.188 0-34.548 15.602-34.548 34.572 0 15.926 10.644 29.4 25.196 33.524 0 0 0.322 0.538 0.808 0.62 1.052 0.216 2.13 1.268 3.26 1.374 14.794 2.83 67.476 13.178 118.702 1.186 91.648-21.396 217.52-110.026 160.904-298.658z" />
<glyph unicode="&#xe622;" d="M1023.972 804.11c0 86.096-69.794 155.89-155.89 155.89h-712.22c-86.096 0-155.89-69.794-155.89-155.89v-712.22c0-86.096 69.794-155.89 155.89-155.89h712.22c86.096 0 155.89 69.794 155.89 155.89v712.22zM133.524 60.794c1.024-12.316-8.218-43.952-20.562-43.952h-2.264c-11.426 0-21.18 28.24-22.284 39.962-17.058 190.14 42.982 344.172 96.66 423.692 19.32 28.916 39.128 52.708 57.074 71.626-4.474 10.24-6.98 21.558-6.98 33.524 0 46.216 37.646 83.592 83.86 83.592 46.484 0 83.7-37.376 83.7-83.592 0-46.51-37.24-83.86-83.7-83.86-17.838 0-34.276 5.632-47.94 15.092-15.872-16.95-33.064-47.884-49.746-73.054-71.114-106.442-100.892-241.986-87.82-383.030zM315.31 328.004c-19.080 0-38.4 2.102-56.832 6.198-12.18 2.884-19.43 15.010-16.924 26.894 2.938 12.26 14.794 19.832 26.892 16.976 15.226-3.504 31.016-5.202 46.86-5.202 114.878 0 208.194 93.32 208.194 207.926 0 114.634-93.32 207.952-208.194 207.952-114.606 0-208.088-93.318-208.088-207.952 0-33.254 7.6-65.268 22.824-94.856 5.848-10.94 1.482-24.522-9.862-30.1-11.076-5.902-24.414-1.536-30.154 9.566-18.296 35.382-27.756 75.508-27.756 115.362 0 139.534 113.474 253.036 253.036 253.036 139.642 0 253.196-113.502 253.196-253.036-0.028-139.292-113.582-252.766-253.198-252.766zM785.866 622.162c-22.42-0.486-45.218 4.204-65.698 13.932-71.464 34.008-101.996 119.726-67.988 191.272 33.9 71.49 119.726 101.996 191.272 67.988 71.49-33.874 102.024-119.836 67.852-191.112-4.958-9.836-10.428-19.212-17.14-27.674-4.23-5.47-12.45-6.466-17.948-2.102s-6.36 12.45-1.994 17.948c5.496 6.846 10.212 14.712 13.958 22.798 28.024 58.718 3.1 129.346-55.808 156.968-58.798 28.024-129.186 3.126-156.968-55.942-28.294-58.664-3.26-129.052 55.7-157.076 17.058-8.004 35.462-11.992 54.056-11.236 7.086 0.134 13.068-5.336 13.204-12.45 0.322-7.114-5.308-12.934-12.504-13.312zM1001.284 534.448c-0.162-0.648-0.432-0.862-0.432-1.24-2.938-5.9-9.674-8.73-16.062-6.466-101.538 37.484-155.972 101.026-183.646 147.86-9.944 16.95-17.164 33.038-22.42 46.7-6.576 0.162-12.854 1.886-18.782 4.5-24.064 11.454-34.062 39.614-22.69 63.408 11.21 23.688 39.478 33.792 63.272 22.42 23.688-11.318 33.658-39.64 22.582-63.272-4.366-9-11.346-16.194-19.482-20.696 5.012-12.476 11.48-26.302 20.318-40.986 37.16-62.41 94.316-108.168 169.58-135.924 6.71-2.614 9.972-9.702 7.76-16.302z" />
<glyph unicode="&#xe623;" d="M512 768c0 0-128 128-512 128v-768c388 0 512-128 512-128s124 128 512 128v768c-384 0-512-128-512-128zM128 768c162.688-13.632 262.496-51.264 320-81.76v-515.488c-57.504 30.368-157.312 68-320 81.76v515.488zM896 252.512c-162.752-13.76-262.496-51.328-320-81.76v515.488c57.504 30.496 157.248 68.128 320 81.76v-515.488z" />
<glyph unicode="&#xe624;" d="M1004.692 493.606l-447.096 447.080c-25.738 25.754-67.496 25.754-93.268 0l-103.882-103.876 78.17-78.17c12.532 5.996 26.564 9.36 41.384 9.36 53.020 0 96-42.98 96-96 0-14.82-3.364-28.854-9.362-41.386l127.976-127.974c12.532 5.996 26.566 9.36 41.386 9.36 53.020 0 96-42.98 96-96s-42.98-96-96-96-96 42.98-96 96c0 14.82 3.364 28.854 9.362 41.386l-127.976 127.974c-3.042-1.456-6.176-2.742-9.384-3.876v-266.968c37.282-13.182 64-48.718 64-90.516 0-53.020-42.98-96-96-96s-96 42.98-96 96c0 41.796 26.718 77.334 64 90.516v266.968c-37.282 13.18-64 48.72-64 90.516 0 14.82 3.364 28.852 9.36 41.384l-78.17 78.17-295.892-295.876c-25.75-25.776-25.75-67.534 0-93.288l447.12-447.080c25.738-25.75 67.484-25.75 93.268 0l445.006 445.006c25.758 25.762 25.758 67.54-0.002 93.29z" />
<glyph unicode="&#xe9d7;" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xf00a;" d="M512 960c-282.75 0-512-229.25-512-512 0-226.25 146.688-418.126 350.156-485.812 25.594-4.688 34.938 11.126 34.938 24.626 0 12.188-0.468 52.562-0.718 95.312-142.376-30.938-172.468 60.376-172.468 60.376-23.312 59.126-56.844 74.876-56.844 74.876-46.532 31.75 3.53 31.126 3.53 31.126 51.406-3.562 78.47-52.75 78.47-52.75 45.688-78.25 119.876-55.626 149-42.5 4.654 33 17.904 55.626 32.5 68.376-113.656 12.938-233.218 56.876-233.218 253.062 0 55.938 19.968 101.562 52.656 137.406-5.218 13-22.844 65.094 5.062 135.562 0 0 42.938 13.75 140.812-52.5 40.812 11.406 84.594 17.032 128.126 17.22 43.5-0.188 87.312-5.876 128.188-17.28 97.688 66.312 140.688 52.5 140.688 52.5 28-70.532 10.376-122.562 5.126-135.5 32.812-35.844 52.626-81.47 52.626-137.406 0-196.688-119.75-240-233.812-252.688 18.438-15.876 34.75-47 34.75-94.75 0-68.438-0.688-123.626-0.688-140.5 0-13.626 9.312-29.562 35.25-24.562 203.312 67.812 349.876 259.688 349.876 485.812 0 282.75-229.25 512-512 512z" />
<glyph unicode="&#xf021;" d="M863.428 356.572q0-2.856-0.572-4-36.572-153.144-153.144-248.286t-273.142-95.144q-83.43 0-161.43 31.428t-139.144 89.714l-73.714-73.714q-10.856-10.856-25.714-10.856t-25.714 10.856-10.856 25.714v256q0 14.856 10.856 25.714t25.714 10.856h256q14.856 0 25.714-10.856t10.858-25.714-10.856-25.714l-78.286-78.286q40.57-37.714 92-58.286t106.858-20.572q76.572 0 142.856 37.144t106.286 102.286q6.286 9.714 30.286 66.856 4.572 13.144 17.144 13.144h109.714q7.428 0 12.856-5.428t5.428-12.856zM877.714 813.714v-256q0-14.856-10.856-25.714t-25.714-10.856h-256q-14.856 0-25.714 10.856t-10.856 25.714 10.856 25.714l78.856 78.856q-84.572 78.286-199.428 78.286-76.572 0-142.856-37.142t-106.286-102.286q-6.286-9.714-30.286-66.858-4.57-13.144-17.144-13.144h-113.714q-7.43 0-12.856 5.428t-5.43 12.856v4q37.144 153.144 154.286 248.286t274.286 95.142q83.428 0 162.286-31.714t140-89.428l74.286 73.714q10.856 10.856 25.714 10.856t25.714-10.856 10.856-25.714z" />
<glyph unicode="&#xf05b;" d="M684 374.856h-62.286q-14.856 0-25.714 10.856t-10.856 25.714v73.144q0 14.856 10.856 25.714t25.714 10.858h62.286q-18.286 61.714-64.286 107.714t-107.714 64.286v-62.286q0-14.858-10.858-25.714t-25.714-10.856h-73.142q-14.856 0-25.714 10.856t-10.858 25.714v62.286q-61.714-18.286-107.714-64.286t-64.286-107.714h62.286q14.858 0 25.714-10.856t10.856-25.714v-73.142q0-14.856-10.856-25.714t-25.714-10.856h-62.286q18.286-61.714 64.286-107.714t107.714-64.286v62.286q0 14.856 10.856 25.714t25.714 10.856h73.142q14.856 0 25.714-10.856t10.858-25.714v-62.286q61.714 18.286 107.714 64.286t64.286 107.714zM877.714 484.572v-73.144q0-14.856-10.856-25.714t-25.714-10.856h-81.714q-21.144-92-88.286-159.144t-159.144-88.286v-81.714q0-14.856-10.856-25.714t-25.714-10.856h-73.142q-14.856 0-25.714 10.856t-10.858 25.714v81.714q-92 21.144-159.144 88.286t-88.286 159.144h-81.714q-14.856 0-25.714 10.856t-10.856 25.714v73.144q0 14.856 10.856 25.714t25.714 10.858h81.714q21.144 92 88.286 159.144t159.144 88.286v81.714q0 14.856 10.856 25.714t25.714 10.856h73.142q14.856 0 25.714-10.856t10.858-25.714v-81.714q92-21.142 159.144-88.286t88.286-159.144h81.714q14.856 0 25.714-10.856t10.856-25.714z" />
<glyph unicode="&#xf0c7;" d="M219.43 82.286h438.858v219.428h-438.858v-219.428zM731.428 82.286h73.144v512q0 8-5.714 22t-11.428 19.714l-160.572 160.57q-5.714 5.714-19.428 11.43t-22.286 5.714v-237.714q0-22.858-16-38.858t-38.856-16h-329.142q-22.856 0-38.856 16t-16 38.856v237.714h-73.144v-731.43h73.144v237.714q0 22.856 16 38.856t38.856 16h475.43q22.856 0 38.856-16t16-38.856v-237.714zM512 612.572v182.856q0 7.428-5.428 12.858t-12.856 5.428h-109.714q-7.428 0-12.858-5.428t-5.428-12.858v-182.856q0-7.428 5.428-12.856t12.856-5.428h109.714q7.428 0 12.858 5.428t5.428 12.856zM877.714 594.286v-530.286q0-22.856-16-38.856t-38.856-16h-768q-22.856 0-38.856 16t-16 38.856v768q0 22.856 16 38.856t38.856 16h530.286q22.856 0 50.286-11.428t43.428-27.428l160-160q16-16 27.428-43.428t11.428-50.286z" />
<glyph unicode="&#xf0ed;" d="M731.429 420.571q0 8-5.143 13.143t-13.143 5.143h-128v201.143q0 7.429-5.429 12.857t-12.857 5.429h-109.714q-7.429 0-12.857-5.429t-5.429-12.857v-201.143h-128q-7.429 0-12.857-5.429t-5.429-12.857q0-8 5.143-13.143l201.143-201.143q5.143-5.143 13.143-5.143t13.143 5.143l200.571 200.571q5.714 6.857 5.714 13.714zM1097.143 292.571q0-90.857-64.286-155.143t-155.143-64.286h-621.714q-105.714 0-180.857 75.143t-75.143 180.857q0 74.286 40 137.143t107.429 94.286q-1.143 17.143-1.143 24.571 0 121.143 85.714 206.857t206.857 85.714q89.143 0 163.143-49.714t107.714-132q40.571 35.429 94.857 35.429 60.571 0 103.429-42.857t42.857-103.429q0-43.429-23.429-78.857 74.286-17.714 122-77.429t47.714-136.286z" horiz-adv-x="1097" />
<glyph unicode="&#xf0ee;" d="M731.429 457.143q0 8-5.143 13.143l-201.143 201.143q-5.143 5.143-13.143 5.143t-13.143-5.143l-200.571-200.571q-5.714-6.857-5.714-13.714 0-8 5.143-13.143t13.143-5.143h128v-201.143q0-7.429 5.429-12.857t12.857-5.429h109.714q7.429 0 12.857 5.429t5.429 12.857v201.143h128q7.429 0 12.857 5.429t5.429 12.857zM1097.143 292.571q0-90.857-64.286-155.143t-155.143-64.286h-621.714q-105.714 0-180.857 75.143t-75.143 180.857q0 74.286 40 137.143t107.429 94.286q-1.143 17.143-1.143 24.571 0 121.143 85.714 206.857t206.857 85.714q89.143 0 163.143-49.714t107.714-132q40.571 35.429 94.857 35.429 60.571 0 103.429-42.857t42.857-103.429q0-43.429-23.429-78.857 74.286-17.714 122-77.429t47.714-136.286z" horiz-adv-x="1097" />
<glyph unicode="&#xf11c;" d="M219.429 283.428v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM292.571 429.714v-54.857q0-9.143-9.143-9.143h-128q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h128q9.143 0 9.143-9.143zM219.429 576v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM804.571 283.428v-54.857q0-9.143-9.143-9.143h-493.714q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h493.714q9.143 0 9.143-9.143zM438.857 429.714v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM365.714 576v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM585.143 429.714v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM512 576v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM731.429 429.714v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM950.857 283.428v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM658.286 576v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM804.571 576v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM950.857 576v-201.143q0-9.143-9.143-9.143h-128q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h64v137.143q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM1024 146.286v512h-950.857v-512h950.857zM1097.143 658.286v-512q0-30.286-21.429-51.714t-51.714-21.429h-950.857q-30.286 0-51.714 21.429t-21.429 51.714v512q0 30.286 21.429 51.714t51.714 21.429h950.857q30.286 0 51.714-21.429t21.429-51.714z" horiz-adv-x="1097" />
<glyph unicode="&#xf148;" d="M581.714 606.286q-10.286-21.143-33.143-21.143h-109.714v-493.714q0-8-5.143-13.143t-13.143-5.143h-402.286q-12 0-16.571 10.286-4.571 11.429 2.286 20l91.429 109.714q5.143 6.286 14.286 6.286h182.857v365.714h-109.714q-22.857 0-33.143 21.143-9.714 21.143 5.143 38.857l182.857 219.429q10.286 12.571 28 12.571t28-12.571l182.857-219.429q15.429-18.286 5.143-38.857z" horiz-adv-x="585" />
<glyph unicode="&#xf149;" d="M18.286 804.571h402.286q7.429 0 12.857-5.429t5.429-13.429v-493.143h109.714q22.857 0 33.143-21.143t-5.143-39.429l-182.857-219.429q-10.286-12.571-28-12.571t-28 12.571l-182.857 219.429q-14.857 17.714-5.143 39.429 10.286 21.143 33.143 21.143h109.714v365.714h-182.857q-8 0-14.286 6.286l-91.429 109.714q-7.429 8-2.286 19.429 5.143 10.857 16.571 10.857z" horiz-adv-x="585" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe600;" glyph-name="tree" horiz-adv-x="1170" d="M0 886.858c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM292.572 960h877.714v-146.286h-877.714zM292.572 594.286c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM585.142 667.428h585.144v-146.286h-585.144zM292.572 9.142c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM585.142 82.286h585.144v-146.286h-585.144zM585.142 301.714c0 40.396 32.747 73.143 73.143 73.143s73.143-32.747 73.143-73.143c0-40.396-32.747-73.143-73.143-73.143s-73.143 32.747-73.143 73.143zM877.714 374.858h292.572v-146.286h-292.572z" />
<glyph unicode="&#xe601;" glyph-name="build" d="M1009.996 131.024l-301.544 301.544c-18.668 18.668-49.214 18.668-67.882 0l-22.626-22.626-184 184 302.056 302.058h-320l-142.058-142.058-14.060 14.058h-67.882v-67.882l14.058-14.058-206.058-206.060 160-160 206.058 206.058 184-184-22.626-22.626c-18.668-18.668-18.668-49.214 0-67.882l301.544-301.544c18.668-18.668 49.214-18.668 67.882 0l113.136 113.136c18.67 18.666 18.67 49.214 0.002 67.882z" />
<glyph unicode="&#xe602;" glyph-name="find" d="M632.576 661.376c46.080-10.666 82.752-49.92 93.418-92.8 3.648-20.054 21.12-35.2 42.048-35.2 23.466 0 42.666 19.2 42.666 42.666 0 30.72-25.814 77.44-61.014 112.426-34.56 34.134-75.734 58.24-109.654 58.24-23.466 0-42.666-19.84-42.666-43.306 0.002-20.884 15.148-38.4 35.202-42.026zM46.122 102.016c-33.066-33.046-33.066-86.826 0-119.872 33.066-33.088 86.826-33.088 119.68 0l245.098 244.906c57.194-35.67 124.586-56.342 197.12-56.342 206.294 0 373.334 167.040 373.334 373.334s-167.040 373.334-373.334 373.334-373.334-167.040-373.334-373.334c0-72.32 20.714-139.946 56.342-197.12l-244.906-244.906zM341.376 544.042c0 147.2 119.488 266.666 266.666 266.666 147.2 0 266.666-119.466 266.666-266.666s-119.466-266.666-266.666-266.666c-147.178 0.022-266.666 119.488-266.666 266.666z" />
<glyph unicode="&#xe603;" glyph-name="findfiles" d="M0 21.334c0-47.146 38.186-85.334 85.334-85.334h277.334c47.146 0 85.334 38.186 85.334 85.334l-0.002 341.332h128v-341.334c0-47.146 38.186-85.334 85.334-85.334h277.334c47.146 0 85.334 38.186 85.334 85.334l-0.002 554.668c0 47.146-38.186 85.334-85.334 85.334h-42.666v192h10.666c29.44 0 53.334 23.892 53.334 53.332s-23.894 53.334-53.334 53.334h-277.334c-29.438 0-53.332-23.894-53.332-53.334s23.894-53.332 53.334-53.332h10.666v-192h-256v192h10.666c29.44 0 53.334 23.892 53.334 53.332s-23.894 53.334-53.334 53.334h-277.334c-29.438 0-53.332-23.894-53.332-53.334s23.894-53.332 53.334-53.332h10.666v-192h-42.666c-47.146 0-85.334-38.186-85.334-85.334v-554.666zM554.666 469.334h-85.334c-23.466 0-42.666 19.2-42.666 42.666s19.2 42.666 42.666 42.666h85.334c23.466 0 42.666-19.2 42.666-42.666s-19.198-42.666-42.666-42.666z" />
<glyph unicode="&#xe604;" glyph-name="editor" d="M118.634 528.918c-20.886-20.886-30.934-50.774-36.032-81.066l-81.322-384.428c-11.52-68.288 56.96-136.982 126.102-126.314l384.874 80.234c28.16 5.098 58.666 13.866 79.574 34.774l407.68 408.128c32.64 32.832 32.64 85.952 0 118.806l-356.054 356.48c-32.854 32.64-85.974 32.64-118.634 0l-406.188-406.614zM509.888 118.678l-199.254-38.208-165.12 165.334 37.952 199.488 296.768 297.174c16.426 16.448 42.88 16.448 59.306 0 16.448-16.426 16.448-43.094 0-59.52l-274.368-274.774c-20.48-20.48-20.48-53.782 0-74.218 20.48-20.502 53.546-20.502 74.026 0l274.582 274.774c16.448 16.426 42.88 16.426 59.306 0 16.448-16.448 16.448-42.902 0-59.306l-274.368-274.774c-20.48-20.48-20.48-53.782 0-74.24 20.48-20.48 53.568-20.48 74.026 0l274.582 274.774c16.234 16.234 42.902 16.234 59.328 0 16.426-16.426 16.426-43.094 0-59.498l-296.766-297.006z" />
<glyph unicode="&#xe605;" glyph-name="report" d="M622.858 799.808l-22.032 110.158h-495.715v-936.349h110.158v385.556h308.443l22.032-110.158h385.556v550.795z" />
<glyph unicode="&#xe606;" glyph-name="signup" d="M697.83 494.458c76.887 0 138.908 62.486 138.908 139.372s-62.021 139.372-138.908 139.372c-76.887 0-139.372-62.486-139.372-139.372s62.486-139.372 139.372-139.372zM326.17 494.458c76.887 0 138.908 62.486 138.908 139.372s-62.021 139.372-138.908 139.372c-76.887 0-139.372-62.486-139.372-139.372s62.486-139.372 139.372-139.372zM326.17 401.542c-108.479 0-325.203-54.355-325.203-162.601v-116.144h650.404v116.144c0 108.245-216.724 162.601-325.203 162.601zM697.83 401.542c-13.472 0-28.571-0.929-44.831-2.555 53.89-38.792 91.289-91.057 91.289-160.047v-116.144h278.744v116.144c0 108.245-216.724 162.601-325.203 162.601z" />
<glyph unicode="&#xe607;" glyph-name="notification" d="M977.76 152.992l-400.608 685.952c-33.408 33.408-87.616 33.408-121.024 0l-400.64-685.952c-33.408-33.376-33.408-87.552 0-120.992h922.24c33.472 33.44 33.472 87.616 0.032 120.992zM479.744 592.704c0 26.528 21.504 48 48 48s48-21.472 48-48v-224c0-26.496-21.504-48-48-48s-48 21.504-48 48v224zM528.032 160.448c-26.496 0-48 21.44-48 48 0 26.496 21.504 48 48 48s48-21.504 48-48c0-26.56-21.504-48-48-48z" />
<glyph unicode="&#xe608;" glyph-name="uniE608" horiz-adv-x="952" d="M438.928 896.072q14.856 0 25.714-10.856t10.858-25.714v-128h168l98.856 98.856q10.856 10.856 25.714 10.856t25.714-10.856 10.856-25.714-10.856-25.714l-98.856-98.856v-482.286l98.856-98.856q10.856-10.856 10.856-25.714t-10.856-25.714-25.714-10.856-25.714 10.856l-98.856 98.856h-168v-128q0-14.856-10.856-25.714t-25.714-10.856-25.714 10.856-10.856 25.714v128q-97.714 0-165.714 38.286l-119.43-118.856q-10.856-10.856-25.714-10.856t-25.714 10.856q-10.856 10.286-10.856 25.714t10.856 25.714l112.57 113.144q-2.856 2.856-7.43 8.572t-16.286 24-20.856 37.144-16.57 46.856-7.43 55.428h512v73.144h-512q0 29.144 7.714 58t18.856 49.714 22.286 37.714 18.57 24.856l8 8.572-118.286 104.57q-12 11.43-12 27.43 0 13.714 9.144 24.57 10.286 10.858 25.43 11.714t26.57-8.856l129.714-115.428q65.144 33.142 156.57 33.142v128q0 14.858 10.858 25.714t25.714 10.856zM768.072 621.786q76 0 129.428-53.428t53.428-129.428-53.428-129.428-129.428-53.428v365.714z" />
<glyph unicode="&#xe609;" glyph-name="max" d="M1024 960v-384l-138.26 138.26-212-212-107.48 107.48 212 212-138.26 138.26zM245.74 821.74l212-212-107.48-107.48-212 212-138.26-138.26v384h384zM885.74 181.74l138.26 138.26v-384h-384l138.26 138.26-212 212 107.48 107.48zM457.74 286.26l-212-212 138.26-138.26h-384v384l138.26-138.26 212 212z" />
<glyph unicode="&#xe60a;" glyph-name="uniE60A" d="M64 384h384v-384l-138.26 138.26-202-202-107.48 107.48 202 202zM821.74 245.74l202-202-107.48-107.48-202 202-138.26-138.26v384h384zM960 512h-384v384l138.26-138.26 202 202 107.48-107.48-202-202zM309.74 757.74l138.26 138.26v-384h-384l138.26 138.26-202 202 107.48 107.48z" />
<glyph unicode="&#xe60b;" glyph-name="remove" d="M800 832h-576c-53.020 0-96-42.98-96-96v-32h768v32c0 53.020-42.98 96-96 96zM632.32 896l14.116-101h-268.872l14.114 101h240.642zM640 960h-256c-26.4 0-50.99-21.392-54.642-47.538l-18.714-133.924c-3.654-26.146 14.956-47.538 41.356-47.538h320c26.4 0 45.010 21.392 41.358 47.538l-18.714 133.924c-3.654 26.146-28.244 47.538-54.644 47.538v0zM816 640h-608c-35.2 0-61.392-28.682-58.206-63.738l52.412-576.526c3.186-35.054 34.594-63.736 69.794-63.736h480c35.2 0 66.608 28.682 69.794 63.736l52.41 576.526c3.188 35.056-23.004 63.738-58.204 63.738zM384 64h-96l-32 448h128v-448zM576 64h-128v448h128v-448zM736 64h-96v448h128l-32-448z" />
<glyph unicode="&#xe60c;" glyph-name="buildrun" d="M192 832l640-384-640-384z" />
<glyph unicode="&#xe60d;" glyph-name="about" d="M708.524 833.167c-54.823 39.545-123.584 59.295-206.423 59.295-63.041 0-116.185-13.935-159.382-41.708-68.567-43.534-104.989-117.434-109.41-221.702h158.852c0 30.367 8.841 59.629 26.572 87.787s47.81 42.236 90.238 42.236c43.101 0 72.844-11.436 89.084-34.26 16.289-22.92 24.41-48.242 24.41-76.015 0-24.169-12.108-46.272-26.714-66.405-8.024-11.726-18.644-22.488-31.759-32.387 0 0-86.154-55.258-124.016-99.657-21.959-25.755-23.929-64.291-25.851-119.598-0.144-3.94 1.346-12.059 15.136-12.059s111.332 0 123.584 0 14.8 9.083 14.991 13.069c0.865 20.133 3.123 30.416 6.823 42.044 6.967 21.959 25.803 41.129 47.042 57.614l43.726 30.176c39.447 30.752 70.97 55.978 84.855 75.775 23.738 32.578 40.409 72.651 40.409 120.171 0 77.601-27.437 136.126-82.166 175.623zM499.553 223.413c-54.777 1.633-99.944-36.231-101.672-95.619-1.73-59.342 41.227-98.549 96.004-100.184 57.18-1.682 101.145 34.981 102.875 94.322 1.682 59.39-40.026 99.8-97.205 101.481z" />
<glyph unicode="&#xe60e;" glyph-name="undo" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
<glyph unicode="&#xe60f;" glyph-name="stop" d="M128 832h768v-768h-768z" />
<glyph unicode="&#xe611;" glyph-name="close" d="M734.668 299.418l-141.21 161.382 141.21 161.382c24.014 24.014 24.014 62.926 0 86.886s-62.924 23.962-86.886 0l-135.782-155.186-135.73 155.136c-24.012 24.012-62.926 24.012-86.886 0s-23.962-62.926 0-86.886l141.158-161.332-141.21-161.382c-23.962-24.014-23.962-62.822 0-86.784 24.012-24.014 62.926-24.014 86.886 0l135.782 155.086 135.73-155.086c24.014-24.014 62.924-24.014 86.886 0s24.014 62.77 0.052 86.784z" />
<glyph unicode="&#xe612;" glyph-name="format" d="M448 165.49l-205.254 237.254 58.508 58.51 146.746-114.744 274.744 242.744 58.512-58.508zM831.772 832c0.078-0.066 0.162-0.15 0.228-0.23v-767.542c-0.066-0.078-0.15-0.162-0.228-0.228h-639.544c-0.080 0.066-0.162 0.15-0.228 0.228v767.544c0.066 0.080 0.15 0.162 0.23 0.228h-128.23v-768c0-70.4 57.6-128 128-128h640c70.4 0 128 57.6 128 128v768h-128.228zM640 832v64c0 35.346-28.654 64-64 64h-128c-35.346 0-64-28.654-64-64v-64h-128v-128h512v128h-128zM576 832h-128v64h128v-64z" />
<glyph unicode="&#xe613;" glyph-name="restore" d="M128 682.666v128h896v-640h-128v-128h-896v640h128zM896 256h42.668v469.334h-725.332v-42.666h682.668v-426.668zM85.332 128h725.332v469.334h-725.332v-469.334z" />
<glyph unicode="&#xe614;" glyph-name="min" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
<glyph unicode="&#xe615;" glyph-name="redo" d="M576 711.628v248.372l384-384-384-384v253.824c-446.75 10.482-427.588-303.792-313.86-509.824-280.712 303.414-221.1 789.57 313.86 775.628z" />
<glyph unicode="&#xe616;" glyph-name="price" d="M501.443 506.062c-119.819 31.143-158.353 63.078-158.353 113.223 0 57.535 53.047 97.916 142.518 97.916 93.957 0 128.794-44.867 131.961-110.848h116.654c-3.431 91.053-59.119 173.925-169.439 201.109v115.599h-158.353v-114.015c-102.402-22.433-184.746-88.413-184.746-190.553 0-121.933 101.082-182.636 248.086-217.999 132.225-31.671 158.353-77.856 158.353-127.474 0-36.159-25.601-94.219-142.518-94.219-108.736 0-151.755 48.826-157.297 110.848h-116.389c6.597-115.599 92.901-180.26 194.511-202.165v-114.544h158.353v113.487c102.667 19.794 184.746 79.178 184.746 187.65 0 149.38-128.266 200.581-248.086 231.987z" />
<glyph unicode="&#xe617;" glyph-name="uniE617" d="M384 448h-320v128h320v128l192-192-192-192zM1024 960v-832l-384-192v192h-384v256h64v-192h320v576l256 128h-576v-256h-64v320z" />
<glyph unicode="&#xe618;" glyph-name="signout" d="M768 320v128h-320v128h320v128l192-192zM704 384v-256h-320v-192l-384 192v832h704v-320h-64v256h-512l256-128v-576h256v192z" />
<glyph unicode="&#xe619;" glyph-name="email" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM256 704h512c9.138 0 18.004-1.962 26.144-5.662l-282.144-329.168-282.144 329.17c8.14 3.696 17.006 5.66 26.144 5.66zM192 256v384c0 1.34 0.056 2.672 0.14 4l187.664-218.942-185.598-185.598c-1.444 5.336-2.206 10.886-2.206 16.54zM768 192h-512c-5.654 0-11.202 0.762-16.54 2.208l182.118 182.118 90.422-105.498 90.424 105.494 182.116-182.12c-5.34-1.44-10.886-2.202-16.54-2.202zM832 256c0-5.654-0.762-11.2-2.206-16.54l-185.6 185.598 187.666 218.942c0.084-1.328 0.14-2.66 0.14-4v-384z" />
<glyph unicode="&#xe61a;" glyph-name="googleplus" d="M437.006 141.838c0-75.068-46.39-134.392-177.758-139.176-76.984 43.786-141.49 106.952-186.908 182.866 23.69 58.496 97.692 103.046 182.316 102.114 24.022-0.252 46.41-4.114 66.744-10.7 55.908-38.866 101-63.152 112.324-107.448 2.114-8.964 3.282-18.206 3.282-27.656zM512 960c-147.94 0-281.196-62.77-374.666-163.098 36.934 20.452 80.538 32.638 126.902 32.638 67.068 0 256.438 0 256.438 0l-57.304-60.14h-67.31c47.496-27.212 72.752-83.248 72.752-145.012 0-56.692-31.416-102.38-75.78-137.058-43.28-33.802-51.492-47.966-51.492-76.734 0-24.542 51.722-61.098 75.5-78.936 82.818-62.112 99.578-101.184 99.578-178.87 0-78.726-68.936-157.104-185.866-183.742 56.348-21.338 117.426-33.048 181.248-33.048 282.77 0 512 229.23 512 512s-229.23 512-512 512zM768 576v-128h-64v128h-128v64h128v128h64v-128h128v-64h-128zM365.768 620.528c11.922-90.776-27.846-149.19-96.934-147.134-69.126 2.082-134.806 65.492-146.74 156.242-11.928 90.788 34.418 160.254 103.53 158.196 69.090-2.074 128.22-76.542 140.144-167.304zM220.886 317.932c-74.68 0-138.128-25.768-182.842-63.864-24.502 59.82-38.044 125.29-38.044 193.932 0 56.766 9.256 111.368 26.312 162.396 7.374-99.442 77.352-176.192 192.97-176.192 8.514 0 16.764 0.442 24.874 1.022-7.95-15.23-13.622-32.19-13.622-49.982 0-29.97 16.488-47.070 36.868-66.894-15.402 0-30.27-0.418-46.516-0.418z" />
<glyph unicode="&#xe61b;" glyph-name="facebook" d="M512 960c282.77 0 512-229.23 512-512 0-261.094-195.438-476.53-448-508.026v380.026h176l16 128h-192v64c0 35.346 28.654 64 64 64h128v128h-128c-106.040 0-192-85.96-192-192v-64h-96v-128h96v-380.026c-252.562 31.496-448 246.932-448 508.026 0 282.77 229.23 512 512 512z" />
<glyph unicode="&#xe61c;" glyph-name="twitter" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM806.242 598.912c0.292-6.508 0.442-13.056 0.442-19.638 0-200.622-152.708-431.964-431.958-431.964-85.736 0-165.536 25.136-232.726 68.214 11.876-1.408 23.962-2.126 36.216-2.126 71.13 0 136.59 24.276 188.55 64.994-66.434 1.22-122.5 45.122-141.824 105.432 9.274-1.768 18.784-2.722 28.566-2.722 13.846 0 27.258 1.856 39.998 5.324-69.452 13.952-121.786 75.312-121.786 148.868 0 0.64 0 1.278 0.016 1.91 20.47-11.37 43.878-18.2 68.764-18.988-40.74 27.222-67.54 73.692-67.54 126.368 0 27.822 7.488 53.904 20.556 76.324 74.878-91.854 186.748-152.292 312.924-158.628-2.588 11.118-3.93 22.702-3.93 34.604 0 83.84 67.98 151.812 151.818 151.812 43.666 0 83.124-18.436 110.818-47.94 34.58 6.808 67.074 19.442 96.412 36.84-11.336-35.454-35.41-65.206-66.752-83.994 30.71 3.668 59.968 11.832 87.194 23.904-20.35-30.448-46.090-57.186-75.758-78.594z" />
<glyph unicode="&#xe61d;" glyph-name="info" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM448 704h128v-128h-128zM640 192h-256v64h64v192h-64v64h192v-256h64z" />
<glyph unicode="&#xe61e;" glyph-name="goline" d="M384 128h640v-128h-640zM384 512h640v-128h-640zM384 896h640v-128h-640zM192 960v-256h-64v192h-64v64zM128 434v-50h128v-64h-192v146l128 60v50h-128v64h192v-146zM256 256v-320h-192v64h128v64h-128v64h128v64h-128v64z" />
<glyph unicode="&#xe61f;" glyph-name="share" d="M864 256c-45.16 0-85.92-18.738-115.012-48.83l-431.004 215.502c1.314 8.252 2.016 16.706 2.016 25.328s-0.702 17.076-2.016 25.326l431.004 215.502c29.092-30.090 69.852-48.828 115.012-48.828 88.366 0 160 71.634 160 160s-71.634 160-160 160-160-71.634-160-160c0-8.622 0.704-17.076 2.016-25.326l-431.004-215.504c-29.092 30.090-69.852 48.83-115.012 48.83-88.366 0-160-71.636-160-160 0-88.368 71.634-160 160-160 45.16 0 85.92 18.738 115.012 48.828l431.004-215.502c-1.312-8.25-2.016-16.704-2.016-25.326 0-88.368 71.634-160 160-160s160 71.632 160 160c0 88.364-71.634 160-160 160z" />
<glyph unicode="&#xe620;" glyph-name="comment" d="M0 343.808l321.344-206.624v149.152l-191.328 118.144 191.328 118.144v150.176l-321.344-207.68v-121.312zM389.312 64h110.592l162.048 768h-111.328l-161.312-768zM702.688 672.8v-150.176l191.264-118.144-191.264-118.112v-149.152l321.312 206.592v121.312l-321.312 207.68z" />
<glyph unicode="&#xe621;" glyph-name="weibo" d="M428.222 493.19c-144.194-6.656-260.742-83.888-260.742-180.224 0-96.202 116.574-168.528 260.742-161.792 144.302 6.576 261.094 96.876 261.094 193.078 0 96.12-116.79 155.594-261.094 148.938zM526.472 250.88c-44.168-56.994-131.53-84.802-216.36-38.858-40.394 21.908-38.884 64.916-38.884 64.916s-16.76 135.842 128.27 152.818c145.192 16.816 171.116-121.856 126.976-178.876zM429.19 341.854c-9.298-6.736-11.182-19.618-6.144-27.62 4.85-8.22 16.142-9.162 25.276-2.318 8.974 7.086 12.45 19.428 7.572 27.62-4.798 7.976-15.952 10.294-26.704 2.318zM360.448 323.154c-27.108-2.802-46.484-26.408-46.484-48.99 0-22.636 21.826-38.264 48.882-35.086 26.974 3.072 48.908 23.928 48.908 46.484 0.054 22.636-20.184 40.582-51.308 37.592zM868.082 960h-712.22c-86.096 0-155.89-69.794-155.89-155.89v-712.22c0-86.096 69.794-155.89 155.89-155.89h712.22c86.096 0 155.89 69.794 155.89 155.89v712.22c0 86.096-69.768 155.89-155.89 155.89zM818.202 280.386c-59.446-126.274-255.462-187.716-400.734-176.344-138.050 10.86-315.526 56.724-333.878 223.798 0 0-9.7 75.668 63.65 173.568 0 0 105.498 147.322 228.378 189.36 122.988 41.85 137.35-28.968 137.35-70.818-6.548-35.49-18.782-56.374 27.38-42.038 0 0 120.886 56.076 170.658 6.332 40.126-40.152 6.63-95.42 6.63-95.42s-16.654-18.404 17.624-25.034c34.358-6.872 142.362-56.914 82.944-183.404zM698.988 629.274c-13.15 0-23.714 10.644-23.714 23.688 0 13.286 10.562 23.956 23.714 23.956 0 0 148.212 27.404 130.48-131.852 0-0.942-0.108-1.698-0.322-2.534-1.672-11.29-11.586-19.94-23.256-19.94-13.204 0-23.956 10.562-23.956 23.74 0-0.026 23.498 106.416-82.944 82.944zM949.518 501.894h-0.216c-3.908-26.948-17.274-29.104-33.198-29.104-19.052 0-34.438 11.964-34.438 31.044 0 16.52 6.846 33.308 6.846 33.308 2.020 6.952 18.136 50.176-10.644 114.796-52.708 88.522-158.856 89.816-171.384 84.776-12.638-4.958-31.286-7.436-31.286-7.436-19.188 0-34.548 15.602-34.548 34.572 0 15.926 10.644 29.4 25.196 33.524 0 0 0.322 0.538 0.808 0.62 1.052 0.216 2.13 1.268 3.26 1.374 14.794 2.83 67.476 13.178 118.702 1.186 91.648-21.396 217.52-110.026 160.904-298.658z" />
<glyph unicode="&#xe623;" glyph-name="book" d="M512 768c0 0-128 128-512 128v-768c388 0 512-128 512-128s124 128 512 128v768c-384 0-512-128-512-128zM128 768c162.688-13.632 262.496-51.264 320-81.76v-515.488c-57.504 30.368-157.312 68-320 81.76v515.488zM896 252.512c-162.752-13.76-262.496-51.328-320-81.76v515.488c57.504 30.496 157.248 68.128 320 81.76v-515.488z" />
<glyph unicode="&#xe624;" glyph-name="git" d="M1004.692 493.606l-447.096 447.080c-25.738 25.754-67.496 25.754-93.268 0l-103.882-103.876 78.17-78.17c12.532 5.996 26.564 9.36 41.384 9.36 53.020 0 96-42.98 96-96 0-14.82-3.364-28.854-9.362-41.386l127.976-127.974c12.532 5.996 26.566 9.36 41.386 9.36 53.020 0 96-42.98 96-96s-42.98-96-96-96-96 42.98-96 96c0 14.82 3.364 28.854 9.362 41.386l-127.976 127.974c-3.042-1.456-6.176-2.742-9.384-3.876v-266.968c37.282-13.182 64-48.718 64-90.516 0-53.020-42.98-96-96-96s-96 42.98-96 96c0 41.796 26.718 77.334 64 90.516v266.968c-37.282 13.18-64 48.72-64 90.516 0 14.82 3.364 28.852 9.36 41.384l-78.17 78.17-295.892-295.876c-25.75-25.776-25.75-67.534 0-93.288l447.12-447.080c25.738-25.75 67.484-25.75 93.268 0l445.006 445.006c25.758 25.762 25.758 67.54-0.002 93.29z" />
<glyph unicode="&#xe900;" glyph-name="qqz" d="M761.562 521.546l-305.314-229.928s122.534-19.085 307.335-16.875l-8.413 38.021 263.313 239.505c4.84 4.404 6.694 11.413 4.708 17.804-1.974 6.394-7.405 10.93-13.783 11.494l-347.126 31.289-135.62 336.932c-2.479 6.184-8.268 10.213-14.657 10.213-6.397 0-12.184-4.029-14.665-10.213l-135.621-336.932-347.128-31.289c-6.374-0.563-11.801-5.1-13.781-11.494-1.987-6.392-0.13-13.401 4.713-17.804l263.31-239.505-78.925-356.295c-1.445-6.521 0.962-13.347 6.142-17.317 5.196-3.9 12.107-4.202 17.608-0.752l298.345 188.885 298.347-188.885c2.554-1.655 5.395-2.401 8.235-2.401 3.299 0 6.583 1.049 9.366 3.146 5.182 3.98 7.593 10.803 6.149 17.325l-62.412 281.762c26.77 14.131 56.254 38.051 56.254 38.051s-116.838-59.919-536.289-30.65l303.994 231.311s-11.952 19.514-392.923 33.442c-25.494 0.938 310.6 66.909 558.84 11.159z" />
<glyph unicode="&#xe9d7;" glyph-name="start" d="M1024 562.95l-353.78 51.408-158.22 320.582-158.216-320.582-353.784-51.408 256-249.538-60.432-352.352 316.432 166.358 316.432-166.358-60.434 352.352 256.002 249.538zM512 206.502l-223.462-117.48 42.676 248.83-180.786 176.222 249.84 36.304 111.732 226.396 111.736-226.396 249.836-36.304-180.788-176.222 42.678-248.83-223.462 117.48z" />
<glyph unicode="&#xf00a;" glyph-name="github" d="M512 960c-282.75 0-512-229.25-512-512 0-226.25 146.688-418.126 350.156-485.812 25.594-4.688 34.938 11.126 34.938 24.626 0 12.188-0.468 52.562-0.718 95.312-142.376-30.938-172.468 60.376-172.468 60.376-23.312 59.126-56.844 74.876-56.844 74.876-46.532 31.75 3.53 31.126 3.53 31.126 51.406-3.562 78.47-52.75 78.47-52.75 45.688-78.25 119.876-55.626 149-42.5 4.654 33 17.904 55.626 32.5 68.376-113.656 12.938-233.218 56.876-233.218 253.062 0 55.938 19.968 101.562 52.656 137.406-5.218 13-22.844 65.094 5.062 135.562 0 0 42.938 13.75 140.812-52.5 40.812 11.406 84.594 17.032 128.126 17.22 43.5-0.188 87.312-5.876 128.188-17.28 97.688 66.312 140.688 52.5 140.688 52.5 28-70.532 10.376-122.562 5.126-135.5 32.812-35.844 52.626-81.47 52.626-137.406 0-196.688-119.75-240-233.812-252.688 18.438-15.876 34.75-47 34.75-94.75 0-68.438-0.688-123.626-0.688-140.5 0-13.626 9.312-29.562 35.25-24.562 203.312 67.812 349.876 259.688 349.876 485.812 0 282.75-229.25 512-512 512z" />
<glyph unicode="&#xf021;" glyph-name="refresh" d="M863.428 356.572q0-2.856-0.572-4-36.572-153.144-153.144-248.286t-273.142-95.144q-83.43 0-161.43 31.428t-139.144 89.714l-73.714-73.714q-10.856-10.856-25.714-10.856t-25.714 10.856-10.856 25.714v256q0 14.856 10.856 25.714t25.714 10.856h256q14.856 0 25.714-10.856t10.858-25.714-10.856-25.714l-78.286-78.286q40.57-37.714 92-58.286t106.858-20.572q76.572 0 142.856 37.144t106.286 102.286q6.286 9.714 30.286 66.856 4.572 13.144 17.144 13.144h109.714q7.428 0 12.856-5.428t5.428-12.856zM877.714 813.714v-256q0-14.856-10.856-25.714t-25.714-10.856h-256q-14.856 0-25.714 10.856t-10.856 25.714 10.856 25.714l78.856 78.856q-84.572 78.286-199.428 78.286-76.572 0-142.856-37.142t-106.286-102.286q-6.286-9.714-30.286-66.858-4.57-13.144-17.144-13.144h-113.714q-7.43 0-12.856 5.428t-5.43 12.856v4q37.144 153.144 154.286 248.286t274.286 95.142q83.428 0 162.286-31.714t140-89.428l74.286 73.714q10.856 10.856 25.714 10.856t25.714-10.856 10.856-25.714z" />
<glyph unicode="&#xf0c7;" glyph-name="save" d="M219.43 82.286h438.858v219.428h-438.858v-219.428zM731.428 82.286h73.144v512q0 8-5.714 22t-11.428 19.714l-160.572 160.57q-5.714 5.714-19.428 11.43t-22.286 5.714v-237.714q0-22.858-16-38.858t-38.856-16h-329.142q-22.856 0-38.856 16t-16 38.856v237.714h-73.144v-731.43h73.144v237.714q0 22.856 16 38.856t38.856 16h475.43q22.856 0 38.856-16t16-38.856v-237.714zM512 612.572v182.856q0 7.428-5.428 12.858t-12.856 5.428h-109.714q-7.428 0-12.858-5.428t-5.428-12.858v-182.856q0-7.428 5.428-12.856t12.856-5.428h109.714q7.428 0 12.858 5.428t5.428 12.856zM877.714 594.286v-530.286q0-22.856-16-38.856t-38.856-16h-768q-22.856 0-38.856 16t-16 38.856v768q0 22.856 16 38.856t38.856 16h530.286q22.856 0 50.286-11.428t43.428-27.428l160-160q16-16 27.428-43.428t11.428-50.286z" />
<glyph unicode="&#xf0ed;" glyph-name="export" horiz-adv-x="1097" d="M731.429 429.714q0 8-5.143 13.143t-13.143 5.143h-128v201.143q0 7.429-5.429 12.857t-12.857 5.429h-109.714q-7.429 0-12.857-5.429t-5.429-12.857v-201.143h-128q-7.429 0-12.857-5.429t-5.429-12.857q0-8 5.143-13.143l201.143-201.143q5.143-5.143 13.143-5.143t13.143 5.143l200.571 200.571q5.714 6.857 5.714 13.714zM1097.143 301.714q0-90.857-64.286-155.143t-155.143-64.286h-621.714q-105.714 0-180.857 75.143t-75.143 180.857q0 74.286 40 137.143t107.429 94.286q-1.143 17.143-1.143 24.571 0 121.143 85.714 206.857t206.857 85.714q89.143 0 163.143-49.714t107.714-132q40.571 35.429 94.857 35.429 60.571 0 103.429-42.857t42.857-103.429q0-43.429-23.429-78.857 74.286-17.714 122-77.429t47.714-136.286z" />
<glyph unicode="&#xf0ee;" glyph-name="import" horiz-adv-x="1097" d="M731.429 466.286q0 8-5.143 13.143l-201.143 201.143q-5.143 5.143-13.143 5.143t-13.143-5.143l-200.571-200.571q-5.714-6.857-5.714-13.714 0-8 5.143-13.143t13.143-5.143h128v-201.143q0-7.429 5.429-12.857t12.857-5.429h109.714q7.429 0 12.857 5.429t5.429 12.857v201.143h128q7.429 0 12.857 5.429t5.429 12.857zM1097.143 301.714q0-90.857-64.286-155.143t-155.143-64.286h-621.714q-105.714 0-180.857 75.143t-75.143 180.857q0 74.286 40 137.143t107.429 94.286q-1.143 17.143-1.143 24.571 0 121.143 85.714 206.857t206.857 85.714q89.143 0 163.143-49.714t107.714-132q40.571 35.429 94.857 35.429 60.571 0 103.429-42.857t42.857-103.429q0-43.429-23.429-78.857 74.286-17.714 122-77.429t47.714-136.286z" />
<glyph unicode="&#xf11c;" glyph-name="keyboard" horiz-adv-x="1097" d="M219.429 292.571v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM292.571 438.857v-54.857q0-9.143-9.143-9.143h-128q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h128q9.143 0 9.143-9.143zM219.429 585.143v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM804.571 292.571v-54.857q0-9.143-9.143-9.143h-493.714q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h493.714q9.143 0 9.143-9.143zM438.857 438.857v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM365.714 585.143v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM585.143 438.857v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM512 585.143v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM731.429 438.857v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM950.857 292.571v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM658.286 585.143v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM804.571 585.143v-54.857q0-9.143-9.143-9.143h-54.857q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM950.857 585.143v-201.143q0-9.143-9.143-9.143h-128q-9.143 0-9.143 9.143v54.857q0 9.143 9.143 9.143h64v137.143q0 9.143 9.143 9.143h54.857q9.143 0 9.143-9.143zM1024 155.429v512h-950.857v-512h950.857zM1097.143 667.429v-512q0-30.286-21.429-51.714t-51.714-21.429h-950.857q-30.286 0-51.714 21.429t-21.429 51.714v512q0 30.286 21.429 51.714t51.714 21.429h950.857q30.286 0 51.714-21.429t21.429-51.714z" />
<glyph unicode="&#xf148;" glyph-name="moveup" horiz-adv-x="585" d="M580.891 578.932q-10.269-21.109-33.090-21.109h-109.537v-492.917q0-7.987-5.135-13.122t-13.122-5.135h-401.637q-11.981 0-16.544 10.269-4.564 11.411 2.282 19.968l91.282 109.537q5.135 6.276 14.263 6.276h182.562v365.124h-109.537q-22.82 0-33.090 21.109-9.698 21.109 5.135 38.794l182.562 219.075q10.269 12.551 27.955 12.551t27.955-12.551l182.562-219.075q15.404-18.257 5.135-38.794z" />
<glyph unicode="&#xf149;" glyph-name="movedown" horiz-adv-x="585" d="M18.286 813.714h402.286q7.429 0 12.857-5.429t5.429-13.429v-493.143h109.714q22.857 0 33.143-21.143t-5.143-39.429l-182.857-219.429q-10.286-12.571-28-12.571t-28 12.571l-182.857 219.429q-14.857 17.714-5.143 39.429 10.286 21.143 33.143 21.143h109.714v365.714h-182.857q-8 0-14.286 6.286l-91.429 109.714q-7.429 8-2.286 19.429 5.143 10.857 16.571 10.857z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 28 KiB

BIN
static/css/fonts/icomoon.ttf Normal file → Executable file

Binary file not shown.

BIN
static/css/fonts/icomoon.woff Normal file → Executable file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -1,100 +0,0 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
body{
overflow: auto;
}
.header {
margin: 0;
padding: 5px;
}
.header li {
margin-left: 10px;
}
.header .gravatar {
width: 26px;
border-radius: 13px;
}
.header .logo {
height: 21px;
margin-top: -6px;
}
.share-panel {
position: absolute;
z-index: 20;
width: 226px;
padding: 5px 0px;
right: 0px;
line-height: normal;
top: 34px;
}
.share-panel .font-ico {
transition: all .2s ease-out 0s;
margin: 0 5px;
width: 24px;
}
.share-panel .font-ico:hover {
transform:rotate(360deg);
}
.footer {
height: 30px;
text-shadow: 0 0 0;
}
#editorDiv {
width: 100%;
height: 70%;
}
#output {
height: 30%;
width: 100%;
border-width: 0;
margin: 0;
padding: 0;
border-top: 1px solid #919191;
}
#dialogShare {
margin: 10px 15px 0;
line-height: 28px;
}
#dialogShare a {
white-space: pre-wrap;
word-wrap: break-word;
}
.wrapper {
width: auto;
}
.btn {
padding: 1px 5px;
}
#disqus_thread {
border-top: 1px solid #919191;
padding: 0 30px;
}

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -15,16 +15,25 @@
*/
body {
overflow: auto;
display: flex;
flex-direction: column;
max-height: 100vh;
}
.main {
flex: 1;
min-height: 1px;
display: flex;
}
.header {
margin: 0;
padding: 10px;
padding: 5px;
}
.font-ico {
font-size: 26px;
.header li {
margin-left: 0;
margin-right: 20px;
}
.header .gravatar {
@ -32,23 +41,25 @@ body {
border-radius: 13px;
}
.header > .fn-right {
margin-top: 10px;
.header .logo {
height: 28px;
margin-top: -6px;
}
.header .logo {
height: 36px;
margin-top: -4px;
.header .font-ico {
font-size: 18px;
line-height: 28px;
}
.share-panel {
position: absolute;
z-index: 20;
width: 258px;
padding: 10px 10px;
width: 190px;
padding: 5px 0px;
right: 0px;
line-height: normal;
top: 57px;
top: 38px;
border-color: #999;
}
.share-panel .font-ico {
@ -58,7 +69,7 @@ body {
}
.share-panel .font-ico:hover {
transform:rotate(360deg);
transform: rotate(360deg);
}
.footer {
@ -67,18 +78,21 @@ body {
}
#editorDiv {
width: 60%;
float: left;
width: 100%;
height: 70%;
}
.bottom-window-group {
height: 30%;
}
#output {
width: 40%;
float: right;
height: 100%;
width: 100%;
border-width: 0;
margin: 0;
padding: 0;
border-left: 1px solid #919191;
font-family: Consolas, 'Courier New', monospace;
padding: 2px 5px;
border-top: 1px solid #919191;
}
#dialogShare {
@ -91,7 +105,54 @@ body {
word-wrap: break-word;
}
#disqus_thread {
border-top: 1px solid #919191;
padding: 0 30px;
.wrapper {
width: auto;
}
.btn {
padding: 3px 8px;
font-size: 13px;
line-height: 20px;
height: 28px;
}
#editorDivWrap {
width: 70%;
}
#goNews {
width: 30%;
overflow: auto;
border-left: 1px solid #919191;
}
#goNews::-webkit-scrollbar {
display: none;
}
#goNews li a {
display: block;
padding: 8px 10px;
text-shadow: 0 1px 0 #fff;
border-bottom: 1px solid #eee;
color: #666;
}
#goNews li a.fn-right {
padding: 0;
border: 0;
color: #4285f4;
}
#goNews li a:hover {
text-decoration: none;
background-color: #f9f9f9;
}
#goNews li.title {
border-bottom: 1px solid #e5e5e5;
padding: 10px;
font-size: 14px;
line-height: 18px;
background-color: #f9fafb;
}

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -14,9 +14,17 @@
* limitations under the License.
*/
body {
display: flex;
min-height: 100vh;
flex-direction: column;
}
.wrapper {
margin: 0 auto;
width: 980px;
display: flex;
align-items: center;
}
.header .logo {
@ -44,15 +52,20 @@
}
.header a:hover {
color: #4183C4;
color: #4285f4;
}
.fn-left {
flex: 1;
}
.content {
border-top: 1px solid #A4A4A4;
border-bottom: 1px solid #919191;
background-color: #202021;
height: 632px;
padding-top: 222px;
background-color: #3b3e43;
flex: 1;
display: flex;
align-items: center;
}
.content h2 {
@ -62,94 +75,104 @@
}
.content h3 {
color: #4183c4;
color: #4285f4;
font-size: 21px;
}
.content .form {
padding: 24px 15px;
background-color: #fff;
border-radius: 6px;
width: 320px;
margin-top: -18px;
position: relative;
}
.content .form input {
border: 1px solid #ccc;
border-radius: 3px;
width: 100%;
background-color: #fafafa;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075) inset;
color: #333;
min-height: 34px;
outline: medium none;
vertical-align: middle;
font-size: 16px;
border: 1px solid #FFF;
padding: 10px;
margin-top: 20px;
.content a {
color: #4285f4;
}
.content .form input:focus {
background-color: #FFF;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.075) inset, 0 0 12px rgba(255, 255, 255, 0.75);
.login__icon {
width: 200px;
transition: all .15s ease-in-out;
padding-right: 24px;
color: #3b3e43;
fill: currentColor;
}
.login__icon:hover {
transform: scale(1.1);
}
.btn {
width: 100%;
color: #fff;
text-shadow: 0 -1px 0 rgba(0,0,0,0.25);
background-color: #60b044;
background-image: linear-gradient(#8add6d, #60b044);
border: 1px solid #5ca941;
background-color: #2ebc4f;
padding: 10px;
border-radius: 3px;
cursor: pointer;
border: 0;
display: block;
text-align: center;
}
.btn:hover {
background-color: #569e3d;
background-image: linear-gradient(#79d858, #569e3d);
border-color: #4a993e;
text-decoration: none;
background-color: #28a745;
}
.btn:focus {
box-shadow: 0 0 0 0.2em rgba(40, 167, 69, .3);
}
.btn-blue {
background-color: #4285f4;
}
.btn-blue:hover {
background-color: #2a75f3;
}
.btn.btn-white,
.btn.btn-red {
color: #333;
text-shadow: 0 1px 0 rgba(255,255,255,0.9);
background-color: #eee;
background-image: linear-gradient(#fcfcfc, #eee);
border-color: #d5d5d5;
background-color: #fff;
}
.btn.btn-red {
color: #9d0000;
color: #d23f31;
}
.btn.btn-white:hover {
color: #333;
text-shadow: 0 1px 0 rgba(255,255,255,0.9);
background-color: #ddd;
background-image: linear-gradient(#eee, #ddd);
border-color: #ccc;
}
.btn.btn-red:hover {
background-color: #b33630;
background-image: linear-gradient(#dc5f59, #b33630);
background-repeat: repeat-x;
border-color: #cd504a;
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.3);
background-color: #d23f31;
}
#msg {
background-color: #fcdede;
border: 1px solid #d2b2b2;
padding: 15px;
font-size: 14px;
color: #911;
position: absolute;
width: 100%;
top: -48px;
.desc {
color: #6a737d;
font-size: 12px;
}
.more-detail {
display: none;
margin: 0 0 8px 60px;
}
.checkbox {
margin-top: 8px;
display: block;
}
.view-more {
cursor: pointer;
display: block;
text-align: center;
color: #4285f4;
margin-top: 8px;
}
.footer {
@ -162,7 +185,7 @@
.footer a {
text-decoration: none;
color: #4183c4;
color: #4285f4;
}
.footer a:hover {
@ -178,7 +201,7 @@
/* start sign up */
.dir {
color: #4183c4;
color: #4285f4;
font-size: 18px;
word-wrap: break-word;
margin-top: 20px;
@ -198,4 +221,21 @@
margin-top: 20px;
font-size: 16px;
}
.start {
text-align: center;
}
.start .btn {
color: #fff;
}
.start svg {
fill: currentColor;
}
.start__aciton {
display: flex;
align-items: center;
}
/* end sign up */

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -39,7 +39,6 @@
margin-bottom: 10px;
padding: 5px 10px;
color: #FFF;
text-shadow: 0 1px rgba(0, 0, 0, 0.4);
}
#startPage .details {

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -136,7 +136,7 @@
.share-panel {
position: absolute;
z-index: 20;
width: 226px;
width: 190px;
padding: 5px 0;
right: 0px;
top: 21px;
@ -259,22 +259,19 @@
.bottom-window-group .output .start-build,
.bottom-window-group .output .start-test, .start-vet,
.bottom-window-group .output .start-install,
.bottom-window-group .output .start-get {
.bottom-window-group .output .start-install {
color: #999;
}
.bottom-window-group .output .build-succ,
.bottom-window-group .output .test-succ, .vet-succ,
.bottom-window-group .output .install-succ,
.bottom-window-group .output .get-succ {
.bottom-window-group .output .install-succ {
color: rgb(0,153,0);
}
.bottom-window-group .output .build-error,
.bottom-window-group .output .test-error, .vet-error,
.bottom-window-group .output .install-error,
.bottom-window-group .output .get-error {
.bottom-window-group .output .install-error {
color: #9d0000;
}

View File

@ -1,6 +1,6 @@
.dialog-close-icon,.dialog-close-icon:hover{text-decoration:none}.dialog-background{height:100%;left:0;opacity:.3;position:absolute;top:0;width:100%;filter:alpha(opacity=30);display:none;background-color:#000;z-index:99}.dialog-panel{position:absolute;z-index:100;display:none;-moz-user-select:none;user-select:none;box-shadow:0 2px 10px 1px #000}.dialog-title{float:left;line-height:22px;margin-left:3px;font-weight:700}.dialog-header-bg{height:23px;background-color:#bbb;cursor:move;width:100%}.dialog-close-icon{float:right;margin:3px}.dialog-main>div{width:100%}.dialog-footer{padding:10px;text-align:right}#dialogCloseEditor button,.dialog-footer button{margin:0 5px}#dialogAlert,#dialogRemoveConfirm,.dialog-form,.dialog-prompt{padding:10px 15px 0;overflow:hidden}.dialog-main input,.dialog-main select{width:100%;margin:2px auto}#dialogGoFilePrompt>ul{position:relative;height:260px;overflow:auto;margin-top:5px;background-color:#FFF;border:1px solid #919191}#dialogPreference{margin:10px}#dialogPreference .tabs-panel{padding:10px}#dialogPreference .preference{margin-bottom:10px}#dialogPreference img.gravatar{width:48px;height:48px}
body,ul{margin:0}body,button,input{font-family:Helvetica}.list li,body{overflow:hidden}::-webkit-scrollbar{background:0 0;width:16px;height:16px}::-webkit-scrollbar-corner{display:none;background-color:transparent}::-webkit-scrollbar-thumb{border:0 solid transparent;border-right-width:4px;border-left-width:4px;border-radius:9px;box-shadow:inset 0 0 0 1px rgba(128,128,128,.2),inset 0 0 0 4px rgba(128,128,128,.2)}::-webkit-scrollbar-thumb:horizontal{border-bottom-width:4px;border-top-width:4px}body{font-size:13px;color:#000}ul{padding:0;list-style:none}*{box-sizing:border-box}a{color:#4183c4;text-decoration:none}a:hover{text-decoration:underline}img{vertical-align:middle}.fn-left{float:left}.fn-right{float:right}.fn-clear:after,.fn-clear:before{display:table;content:""}.fn-clear:after{clear:both}.fn-none{display:none}.ft-small{color:#999;font-size:12px}.ft-red{color:#9d0000}.list li{cursor:pointer;line-height:20px;padding:0 3px;word-wrap:normal;word-break:normal;white-space:nowrap;text-overflow:ellipsis}.list li.selected,.list li:hover{background-color:#3875d7;color:#FFF}.list li.selected .ft-small,.list li:hover .ft-small{color:#FFF}@font-face{font-family:icomoon;src:url(fonts/icomoon.eot?35cb2z);src:url(fonts/icomoon.eot?#iefix35cb2z) format('embedded-opentype'),url(fonts/icomoon.woff?35cb2z) format('woff'),url(fonts/icomoon.ttf?35cb2z) format('truetype'),url(fonts/icomoon.svg?35cb2z#icomoon) format('svg');font-weight:400;font-style:normal}.font-ico,[class^=ico-]{font-family:icomoon;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;cursor:pointer;font-size:13px;line-height:20px}.ico-book:before{content:"\e623"}.ico-price:before{content:"\e616"}.ico-start:before{content:"\e9d7"}.ico-share:before{content:"\e61f"}.ico-github:before{content:"\f00a"}.ico-git:before{content:"\e624"}.ico-tencent:before{content:"\e622"}.ico-weibo:before{content:"\e621"}.ico-googleplus:before{content:"\e61a"}.ico-twitter:before{content:"\e61c"}.ico-email:before{content:"\e619"}.ico-facebook:before{content:"\e61b"}.ico-moveup:before{content:"\f148"}.ico-movedown:before{content:"\f149"}.ico-keyboard:before{content:"\f11c"}.ico-findfiles:before{content:"\e603"}.ico-find:before{content:"\e602"}.ico-editor:before{content:"\e604"}.ico-tree:before{content:"\e600"}.ico-build:before{content:"\e601"}.ico-notification:before{content:"\e607"}.ico-report:before{content:"\e605"}.ico-comment:before{content:"\e620"}.ico-goline:before{content:"\e61e"}.ico-info:before{content:"\e61d"}.ico-signup:before{content:"\e606"}.ico-signout:before{content:"\e618"}.ico-redo:before{content:"\e615"}.ico-undo:before{content:"\e60e"}.ico-about:before{content:"\e60d"}.ico-import:before{content:"\f0ee"}.ico-export:before{content:"\f0ed"}.ico-refresh:before{content:"\f021"}.ico-remove:before{content:"\e60b"}.ico-save:before{content:"\f0c7"}.ico-max:before{content:"\e609"}.ico-format:before{content:"\e612"}.ico-buildrun:before{content:"\e60c"}.ico-stop:before{content:"\e60f"}.ico-restore:before{content:"\e613"}.toolbars .ico-restore:before{content:"\e60a"}.ico-min:before{content:"\e614";position:absolute;right:5px}.ico-close:before{content:"\e611"}
.frame li,.tabs>div{padding:0 5px;cursor:pointer}.footer .cursor,.frame li,.menu>ul>li>span,.notification-count,.tabs>div{cursor:pointer}.ico,.menu .split,.menu>ul>li,.tabs>div{float:left}.frame{position:absolute;width:320px;z-index:21;display:none}.frame li{line-height:25px}.frame li.disabled,.frame li.disabled .font-ico,.frame li.disabled:hover .font-ico{color:#999}.frame a{color:#000;text-decoration:none}.frame a:hover,.frame li:hover a{color:#FFF}.frame .space{display:inline-block;width:20px;height:15px}.frame .font-ico{margin-right:5px;width:15px;display:inline-block;text-align:center}.tabs{height:21px;overflow:hidden;width:100%}.tabs>div{line-height:20px;height:20px}.tabs>div>span.changed{font-weight:700}.tabs-panel{overflow:auto;flex:1;height:100%}.edit-exprinfo,.edit-panel{position:absolute;overflow:hidden}.menu{display:block!important}.menu>ul>li>span{font-size:12px;line-height:21px;padding:4px 7px}.menu .split{border-left:1px solid #919191;height:21px;margin:0 5px 0 0}.menu img.gravatar{float:left;margin:2px 8px;height:17px;width:17px;border-radius:9px}#buildRun{color:#6DB14C;font-size:19px}#buildRun.ico-stop{color:#9d0000;font-size:16px}.share-panel{position:absolute;z-index:20;width:226px;padding:5px 0;right:0;top:21px}.share-panel .font-ico{font-size:20px;transition:all .2s ease-out 0s;margin:0 5px;width:24px}.share-panel .font-ico:hover{transform:rotate(360deg)}.edit-panel{left:20%;width:60%;height:70%;flex-flow:column;display:flex}.toolbars{position:absolute;right:5px;top:1px}.ico{background-image:url(../images/ico-file.png);height:16px;margin:2px 0 0 -2px;width:16px}.edit-exprinfo{z-index:10;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:#fff;font-size:90%;max-height:20em;overflow-y:auto}.CodeMirror,.CodeMirror-hints{font-family:Consolas,'Courier New',monospace}.CodeMirror-hints .ico{margin:-1px 2px 0 -1px}.CodeMirror-focused .cm-matchhighlight{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);background-position:bottom;background-repeat:repeat-x}.CodeMirror-hint{padding-right:18px;max-width:none}.CodeMirror-hint:hover{background:#08f;color:#fff}.CodeMirror div.CodeMirror-cursor{border-left:2px solid #333}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:transparent}.bottom-window-group{background-color:#fff;flex-flow:column}.bottom-window-group .output{font-family:Consolas,Courier New,monospace;padding:0 5px;line-height:16px;font-size:12px;overflow-x:scroll;outline:0}.bottom-window-group .output pre{margin:0;font-family:Consolas,'Courier New',monospace}.bottom-window-group .output .start-build,.bottom-window-group .output .start-get,.bottom-window-group .output .start-install,.bottom-window-group .output .start-test,.start-vet{color:#999}.bottom-window-group .output .build-succ,.bottom-window-group .output .get-succ,.bottom-window-group .output .install-succ,.bottom-window-group .output .test-succ,.vet-succ{color:#090}.bottom-window-group .output .build-error,.bottom-window-group .output .get-error,.bottom-window-group .output .install-error,.bottom-window-group .output .test-error,.vet-error{color:#9d0000}.bottom-window-group .output .stderr{color:gray;font-style:italic}.bottom-window-group .output .path{text-decoration:underline;cursor:pointer}.bottom-window-group table{width:100%}.bottom-window-group td{border-bottom:1px solid #919191;font-size:12px;line-height:19px}.bottom-window-group .notification{outline:0}.bottom-window-group .notification .severity,.bottom-window-group .notification .type{width:50px;padding:0 5px}.bottom-window-group .search{display:flex;flex-flow:column;outline:0}.footer{box-shadow:0 1px 0 0 rgba(255,255,255,.06) inset;padding-left:5px;line-height:18px;display:block!important}.notification-count{float:right;display:none;background-color:#9d0000;color:#FFF;margin:1px 5px;padding:0 2px;border-radius:3px;line-height:16px}
.side{width:20%;position:absolute;height:100%;z-index:8;flex-flow:column;display:flex}.side-max{width:100%;z-index:11}.side-right .tabs-panel>div{overflow:auto}.side-right{flex-flow:column}#outline .ico{margin:1px 5px 0}.ico-func{background-position:-123px -21px}.ico-interface{background-position:-143px -21px}.ico-const{background-position:-103px -21px}.ico-var{background-position:-63px -21px}.ico-struct{background-position:-83px -21px}.ico-type{background-position:-163px -21px}.ico-package{background-position:-183px -21px}.ztree{width:100%;padding:0;outline:0;border:0}.ztree li a.curSelectedNode{background-color:#3875d7;border-width:0;color:#fff;height:18px;opacity:1}.ztree li a:hover{text-decoration:none}.ztree li>a>span.button,.ztree li>a>span.button.ico-ztree-dir,.ztree li>a>span.button.ico-ztree-dir-api,.ztree li>a>span.button.ico-ztree-dir-workspace{margin-right:2px}.ztree li>a>span.button{background-image:url(../images/ico-file.png);margin-right:0}.ico-ztree-dir{background-position:-2px -23px}.ico-ztree-dir-api{background-position:-22px -23px}.ico-ztree-dir-workspace{background-position:-42px -23px}.ico-ztree-html{background-position:-4px -2px}.ico-ztree-go{background-position:-22px -2px}.ico-ztree-css{background-position:-42px -2px}.ico-ztree-img{background-position:-63px -2px}.ico-ztree-other{background-position:-83px -2px}.ico-ztree-text{background-position:-103px -2px}.ico-ztree-sql{background-position:-123px -2px}.ico-ztree-pro{background-position:-142px -2px}.ico-ztree-md{background-position:-162px -2px}.ico-ztree-js{background-position:-182px -2px}.ico-ztree-xml{background-position:-202px -2px}
#startPage{padding:50px 70px;line-height:28px;white-space:normal;word-wrap:break-word;overflow:auto}#startPage a{color:#4183c4;text-decoration:none}#startPage a:hover{text-decoration:underline}#startPage .title{background-color:#BBB;border-bottom-width:0!important;border-radius:3px 3px 0 0;font-size:15px;margin-bottom:10px;padding:5px 10px;color:#FFF;text-shadow:0 1px rgba(0,0,0,.4)}#startPage .details li.border,#startPage .news li{border-bottom:1px solid #919191}#startPage .details{width:30%;float:left}#startPage .details label{color:#666}#startPage .details li.border{padding-bottom:5px;margin-bottom:5px}#startPage .details li.border.workspace{line-height:18px;padding-bottom:10px!important;word-wrap:break-word;white-space:normal;word-break:break-all}#startPage .news{width:60%;float:right;border-left:1px solid #f1f1f1;margin-left:10%;padding-left:10%;white-space:nowrap;overflow:hidden}#startPage .date{color:#bbb;font-size:13px;word-wrap:normal;white-space:nowrap}
#dialogAboutDialog .dialog-main{background-color:#FFF}#dialogAbout{margin:35px 20px;line-height:28px}#dialogAbout .item{margin:0 10px}#dialogAbout a{color:#4183c4;text-decoration:none}#dialogAbout a:hover{text-decoration:underline}#dialogAbout label{color:#666}#dialogAbout img{width:100px;float:left;margin-right:60px}#dialogAbout .space{margin-bottom:6px;border-bottom:1px solid #919191;padding-bottom:6px}#dialogAbout .thx ul{margin-left:50px}#dialogAbout .thx a{width:80px;display:inline-block}#dialogAbout .license{color:#999;font-size:12px;line-height:normal;height:85px;overflow-x:hidden;word-wrap:break-word}
.dialog-background{height:100%;left:0;opacity:.3;position:absolute;top:0;width:100%;display:none;background-color:#000;z-index:99}.dialog-panel{position:absolute;z-index:100;display:none;-moz-user-select:none;user-select:none;box-shadow:0 2px 10px 1px #000}.dialog-title{float:left;line-height:22px;margin-left:3px;font-weight:700}.dialog-header-bg{height:23px;background-color:#bbb;cursor:move;width:100%}.dialog-close-icon{float:right;margin:3px;text-decoration:none}.dialog-close-icon:hover{text-decoration:none}.dialog-main>div{width:100%}.dialog-footer{padding:10px;text-align:right}#dialogCloseEditor button,.dialog-footer button{margin:0 5px}#dialogAlert,#dialogRemoveConfirm,.dialog-form,.dialog-prompt{padding:10px 15px 0;overflow:hidden}.dialog-main input,.dialog-main select{width:100%;margin:2px auto}#dialogGoFilePrompt>ul{position:relative;height:260px;overflow:auto;margin-top:5px;background-color:#fff;border:1px solid #919191}#dialogPreference{margin:10px}#dialogPreference .tabs-panel{padding:10px}#dialogPreference .preference{margin-bottom:10px}#dialogPreference img.gravatar{width:48px;height:48px}
::-webkit-scrollbar{background:0 0;width:16px;height:16px}::-webkit-scrollbar-corner{display:none;background-color:transparent}::-webkit-scrollbar-thumb{border:solid 0 transparent;border-right-width:4px;border-left-width:4px;border-radius:9px;box-shadow:inset 0 0 0 1px rgba(128,128,128,.2),inset 0 0 0 4px rgba(128,128,128,.2)}::-webkit-scrollbar-thumb:horizontal{border-bottom-width:4px;border-top-width:4px}body{font-size:13px;margin:0;color:#000;overflow:hidden;font-family:Helvetica}ul{padding:0;margin:0;list-style:none}*{box-sizing:border-box}a{color:#4183c4;text-decoration:none}a:hover{text-decoration:underline}img{vertical-align:middle}button,input{font-family:Helvetica}.fn-left{float:left}.fn-right{float:right}.fn-clear:after,.fn-clear:before{display:table;content:""}.fn-clear:after{clear:both}.fn-none{display:none}.ft-small{color:#999;font-size:12px}.ft-red{color:#9d0000}.list li{cursor:pointer;line-height:20px;padding:0 3px;word-wrap:normal;word-break:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.list li.selected,.list li:hover{background-color:#3875d7;color:#fff}.list li.selected .ft-small,.list li:hover .ft-small{color:#fff}@font-face{font-family:icomoon;src:url(fonts/icomoon.eot?lqk80d);src:url(fonts/icomoon.eot?lqk80d#iefix) format('embedded-opentype'),url(fonts/icomoon.ttf?lqk80d) format('truetype'),url(fonts/icomoon.woff?lqk80d) format('woff'),url(fonts/icomoon.svg?lqk80d#icomoon) format('svg');font-weight:400;font-style:normal}[class*=" ico-"],[class^=ico-]{font-family:icomoon!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;cursor:pointer;font-size:13px;line-height:20px}.ico-qqz:before{content:"\e900"}.ico-find:before{content:"\e602"}.ico-findfiles:before{content:"\e603"}.ico-editor:before{content:"\e604"}.ico-notification:before{content:"\e607"}.ico-price:before{content:"\e616"}.ico-report:before{content:"\e605"}.ico-git:before{content:"\e624"}.ico-book:before{content:"\e623"}.ico-start:before{content:"\e9d7";text-shadow:0 0 rgba(0,0,0,.4)}.ico-tree:before{content:"\e600"}.ico-build:before{content:"\e601"}.ico-export:before{content:"\f0ed"}.ico-import:before{content:"\f0ee"}.ico-keyboard:before{content:"\f11c"}.ico-moveup:before{content:"\f148"}.ico-movedown:before{content:"\f149"}.ico-weibo:before{content:"\e621"}.ico-uniE608:before{content:"\e608"}.ico-max:before{content:"\e609"}.ico-remove:before{content:"\e60b"}.ico-buildrun:before{content:"\e60c"}.ico-about:before{content:"\e60d"}.ico-undo:before{content:"\e60e"}.ico-stop:before{content:"\e60f"}.ico-close:before{content:"\e611";text-shadow:0 0 rgba(0,0,0,.4)}.ico-format:before{content:"\e612"}.ico-restore:before{content:"\e613"}.toolbars .ico-restore:before{content:"\e60a"}.ico-min:before{content:"\e614";position:absolute;right:5px}.ico-redo:before{content:"\e615"}.ico-uniE617:before{content:"\e617"}.ico-signout:before{content:"\e618"}.ico-email:before{content:"\e619"}.ico-googleplus:before{content:"\e61a"}.ico-facebook:before{content:"\e61b"}.ico-twitter:before{content:"\e61c"}.ico-info:before{content:"\e61d"}.ico-goline:before{content:"\e61e"}.ico-share:before{content:"\e61f"}.ico-comment:before{content:"\e620"}.ico-github:before{content:"\f00a"}.ico-refresh:before{content:"\f021"}.ico-save:before{content:"\f0c7"}
.frame{position:absolute;width:320px;z-index:21;display:none}.frame li{padding:0 5px;line-height:25px;cursor:pointer}.frame li.disabled,.frame li.disabled .font-ico,.frame li.disabled:hover .font-ico{color:#999}.frame a{color:#000;text-decoration:none}.frame a:hover,.frame li:hover a{color:#fff}.frame .space{display:inline-block;width:20px;height:15px}.frame .font-ico{margin-right:5px;width:15px;display:inline-block;text-align:center}.tabs{height:21px;overflow:hidden;width:100%}.tabs>div{float:left;line-height:20px;height:20px;padding:0 5px;cursor:pointer}.tabs>div>span.changed{font-weight:700}.tabs-panel{overflow:auto;flex:1;height:100%}.menu{display:block!important}.menu>ul>li{float:left}.menu>ul>li>span{font-size:12px;line-height:21px;cursor:pointer;padding:4px 7px}.menu .split{float:left;border-left:1px solid #919191;height:21px;margin:0 5px 0 0}.menu img.gravatar{float:left;margin:2px 8px;height:17px;width:17px;border-radius:9px}#buildRun{color:#6db14c;font-size:19px}#buildRun.ico-stop{color:#9d0000;font-size:16px}.share-panel{position:absolute;z-index:20;width:190px;padding:5px 0;right:0;top:21px}.share-panel .font-ico{font-size:20px;transition:all .2s ease-out 0s;margin:0 5px;width:24px}.share-panel .font-ico:hover{transform:rotate(360deg)}.edit-panel{position:absolute;left:20%;width:60%;height:70%;overflow:hidden;flex-flow:column;display:flex}.toolbars{position:absolute;right:5px;top:1px}.ico{background-image:url(../images/ico-file.png);float:left;height:16px;margin:2px 0 0 -2px;width:16px}.edit-exprinfo{position:absolute;z-index:10;overflow:hidden;list-style:none;margin:0;padding:2px;-webkit-box-shadow:2px 3px 5px rgba(0,0,0,.2);-moz-box-shadow:2px 3px 5px rgba(0,0,0,.2);box-shadow:2px 3px 5px rgba(0,0,0,.2);border-radius:3px;border:1px solid silver;background:#fff;font-size:90%;max-height:20em;overflow-y:auto}.CodeMirror,.CodeMirror-hints{font-family:Consolas,'Courier New',monospace}.CodeMirror-hints .ico{margin:-1px 2px 0 -1px}.CodeMirror-focused .cm-matchhighlight{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);background-position:bottom;background-repeat:repeat-x}.CodeMirror-hint{padding-right:18px;max-width:none}.CodeMirror-hint:hover{background:#08f;color:#fff}.CodeMirror div.CodeMirror-cursor{border-left:2px solid #333}.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler{background-color:transparent}.bottom-window-group{background-color:#fff;flex-flow:column}.bottom-window-group .output{font-family:Consolas,Courier New,monospace;padding:0 5px;line-height:16px;font-size:12px;overflow-x:scroll;outline:0}.bottom-window-group .output pre{margin:0;font-family:Consolas,'Courier New',monospace}.bottom-window-group .output .start-build,.bottom-window-group .output .start-install,.bottom-window-group .output .start-test,.start-vet{color:#999}.bottom-window-group .output .build-succ,.bottom-window-group .output .install-succ,.bottom-window-group .output .test-succ,.vet-succ{color:#090}.bottom-window-group .output .build-error,.bottom-window-group .output .install-error,.bottom-window-group .output .test-error,.vet-error{color:#9d0000}.bottom-window-group .output .stderr{color:gray;font-style:italic}.bottom-window-group .output .path{text-decoration:underline;cursor:pointer}.bottom-window-group table{width:100%}.bottom-window-group td{border-bottom:1px solid #919191;font-size:12px;line-height:19px}.bottom-window-group .notification{outline:0}.bottom-window-group .notification .severity,.bottom-window-group .notification .type{width:50px;padding:0 5px}.bottom-window-group .search{display:flex;flex-flow:column;outline:0}.footer{box-shadow:0 1px 0 0 rgba(255,255,255,.06) inset;padding-left:5px;line-height:18px;display:block!important}.footer .cursor{cursor:pointer}.notification-count{float:right;display:none;cursor:pointer;background-color:#9d0000;color:#fff;margin:1px 5px;padding:0 2px;border-radius:3px;line-height:16px}
.side{width:20%;position:absolute;height:100%;z-index:8;flex-flow:column;display:flex}.side-max{width:100%;z-index:11}.side-right .tabs-panel>div{overflow:auto}.side-right{flex-flow:column}#outline .ico{margin:1px 5px 0 5px}.ico-func{background-position:-123px -21px}.ico-interface{background-position:-143px -21px}.ico-const{background-position:-103px -21px}.ico-var{background-position:-63px -21px}.ico-struct{background-position:-83px -21px}.ico-type{background-position:-163px -21px}.ico-package{background-position:-183px -21px}.ztree{width:100%;padding:0;outline:0;border:0}.ztree li a.curSelectedNode{background-color:#3875d7;border-width:0;color:#fff;height:18px;opacity:1}.ztree li a:hover{text-decoration:none}.ztree li>a>span.button,.ztree li>a>span.button.ico-ztree-dir,.ztree li>a>span.button.ico-ztree-dir-api,.ztree li>a>span.button.ico-ztree-dir-workspace{margin-right:2px}.ztree li>a>span.button{background-image:url(../images/ico-file.png);margin-right:0}.ico-ztree-dir{background-position:-2px -23px}.ico-ztree-dir-api{background-position:-22px -23px}.ico-ztree-dir-workspace{background-position:-42px -23px}.ico-ztree-html{background-position:-4px -2px}.ico-ztree-go{background-position:-22px -2px}.ico-ztree-css{background-position:-42px -2px}.ico-ztree-img{background-position:-63px -2px}.ico-ztree-other{background-position:-83px -2px}.ico-ztree-text{background-position:-103px -2px}.ico-ztree-sql{background-position:-123px -2px}.ico-ztree-pro{background-position:-142px -2px}.ico-ztree-md{background-position:-162px -2px}.ico-ztree-js{background-position:-182px -2px}.ico-ztree-xml{background-position:-202px -2px}
#startPage{padding:50px 70px;line-height:28px;white-space:normal;word-wrap:break-word;overflow:auto}#startPage a{color:#4183c4;text-decoration:none}#startPage a:hover{text-decoration:underline}#startPage .title{background-color:#bbb;border-bottom-width:0!important;border-radius:3px 3px 0 0;font-size:15px;margin-bottom:10px;padding:5px 10px;color:#fff}#startPage .details{width:30%;float:left}#startPage .details label{color:#666}#startPage .details li.border{padding-bottom:5px;margin-bottom:5px;border-bottom:1px solid #919191}#startPage .details li.border.workspace{line-height:18px;padding-bottom:10px!important;word-wrap:break-word;white-space:normal;word-break:break-all}#startPage .news{width:60%;float:right;border-left:1px solid #f1f1f1;margin-left:10%;padding-left:10%;white-space:nowrap;overflow:hidden}#startPage .news li{border-bottom:1px solid #919191}#startPage .date{color:#bbb;font-size:13px;word-wrap:normal;white-space:nowrap}
#dialogAboutDialog .dialog-main{background-color:#fff}#dialogAbout{margin:35px 20px;line-height:28px}#dialogAbout .item{margin:0 10px}#dialogAbout a{color:#4183c4;text-decoration:none}#dialogAbout a:hover{text-decoration:underline}#dialogAbout label{color:#666}#dialogAbout img{width:100px;float:left;margin-right:60px}#dialogAbout .space{margin-bottom:6px;border-bottom:1px solid #919191;padding-bottom:6px}#dialogAbout .thx ul{margin-left:50px}#dialogAbout .thx a{width:80px;display:inline-block}#dialogAbout .license{color:#999;font-size:12px;line-height:normal;height:85px;overflow-x:hidden;word-wrap:break-word}

BIN
static/images/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
static/images/hacpai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -19,7 +19,7 @@
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.1.0.1, Dec 8, 2015
* @version 1.1.1.1, Mar 15, 2017
*/
var bottomGroup = {
tabs: undefined,
@ -82,6 +82,7 @@ var bottomGroup = {
fillOutput: function (data) {
var $output = $('.bottom-window-group .output');
data = data.replace(/\r/g, '');
data = data.replace(/\n/g, '<br/>');
if (-1 !== data.indexOf("<br/>")) {

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -24,7 +24,7 @@
$.fn.extend({
dialog: {
version: "0.0.1.7",
author: "lly219@gmail.com"
author: "v@b3log.org"
}
});

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -178,7 +178,7 @@ var editors = {
}
if (editors.data.length === 0) { // 起始页可能存在,所以用编辑器数据判断
menu.disabled(['save-all', 'build', 'run', 'go-test', 'go-vet', 'go-get', 'go-install',
menu.disabled(['save-all', 'build', 'run', 'go-test', 'go-vet', 'go-mod', 'go-install',
'find', 'find-next', 'find-previous', 'replace', 'replace-all',
'format', 'autocomplete', 'jump-to-decl', 'expr-info', 'find-usages', 'toggle-comment',
'edit']);
@ -246,9 +246,9 @@ var editors = {
+ '"><span class="ico-start font-ico"></span> ' + config.label.start_page + '</span>',
content: '<div id="startPage"></div>',
after: function () {
$("#startPage").load(config.context + '/start?sid=' + config.wideSessionId);
$("#startPage").load('/start?sid=' + config.wideSessionId);
$.ajax({
url: "https://symphony.b3log.org/apis/articles?tags=wide,golang&p=1&size=20",
url: "https://ld246.com/apis/articles?tags=wide,golang&p=1&size=20",
type: "GET",
dataType: "jsonp",
jsonp: "callback",
@ -264,7 +264,8 @@ var editors = {
length = 9;
}
var listHTML = "<ul><li class='title'>" + config.label.community + "</li>";
var listHTML = "<ul><li class='title'>" + config.label.community +
"<a href='https://ld246.com/article/1437497122181' target='_blank' class='fn-right'>边看边练</li>";
for (var i = 0; i < length; i++) {
var article = articles[i];
listHTML += "<li>"
@ -328,7 +329,7 @@ var editors = {
$.ajax({
async: false, // 同步执行
type: 'POST',
url: config.context + '/autocomplete',
url: '/autocomplete',
data: JSON.stringify(request),
dataType: "json",
success: function (data) {
@ -434,11 +435,11 @@ var editors = {
$.ajax({
type: 'POST',
url: config.context + '/exprinfo',
url: '/exprinfo',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -582,11 +583,11 @@ var editors = {
$.ajax({
type: 'POST',
url: config.context + '/find/decl',
url: '/find/decl',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -612,11 +613,11 @@ var editors = {
$.ajax({
type: 'POST',
url: config.context + '/find/usages',
url: '/find/usages',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -731,7 +732,7 @@ var editors = {
content: '<textarea id="editor' + id + '"></textarea>'
});
menu.undisabled(['save-all', 'close-all', 'build', 'run', 'go-test', 'go-vet', 'go-get', 'go-install',
menu.undisabled(['save-all', 'close-all', 'build', 'run', 'go-test', 'go-vet', 'go-mod', 'go-install',
'find', 'find-next', 'find-previous', 'replace', 'replace-all',
'format', 'autocomplete', 'jump-to-decl', 'expr-info', 'find-usages', 'toggle-comment',
'edit']);

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

124
static/js/lib.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -73,7 +73,7 @@ xquery version &quot;1.0-ml&quot;;
: you may not use this file except in compliance with the License.
: You may obtain a copy of the License at
:
: http://www.apache.org/licenses/LICENSE-2.0
: https://www.apache.org/licenses/LICENSE-2.0
:
: Unless required by applicable law or agreed to in writing, software
: distributed under the License is distributed on an &quot;AS IS&quot; BASIS,

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -19,7 +19,7 @@
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Dec 8, 2015
* @version 1.0.1.3, Oct 5, 2018
*/
var menu = {
init: function () {
@ -55,14 +55,13 @@ var menu = {
var title = encodeURIComponent($('title').text() + '. \n' + $('meta[name=description]').attr('content')
+ " #golang#");
urls.weibo = "http://v.t.sina.com.cn/share/share.php?title=" + title + "&url=" + url + "&pic=" + pic;
urls.tencent = "http://share.v.t.qq.com/index.php?c=share&a=index&title=" + title +
"&url=" + url + "&pic=" + pic;
urls.qqz = "https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=" + url + "&sharesource=qzone&title=" + title+ "&pics=" + pic;
window.open(urls[key], "_blank", "top=100,left=200,width=648,height=618");
});
},
_initAbout: function () {
$("#dialogAbout").load(config.context + '/about', function () {
$("#dialogAbout").load('/about', function () {
$("#dialogAbout").dialog({
"modal": true,
"title": config.label.about,
@ -160,11 +159,11 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/logout',
url: '/logout',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
window.location.href = "/login";
}
}
@ -173,33 +172,6 @@ var menu = {
openAbout: function () {
$("#dialogAbout").dialog("open");
},
goget: function () {
menu.saveAllFiles();
var currentPath = editors.getCurrentPath();
if (!currentPath) {
return false;
}
if ($(".menu li.go-get").hasClass("disabled")) {
return false;
}
var request = newWideRequest();
request.file = currentPath;
$.ajax({
type: 'POST',
url: config.context + '/go/get',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
bottomGroup.resetOutput();
},
success: function (result) {
}
});
},
goinstall: function () {
menu.saveAllFiles();
@ -217,7 +189,7 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/go/install',
url: '/go/install',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
@ -245,7 +217,7 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/go/test',
url: '/go/test',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
@ -273,7 +245,7 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/go/vet',
url: '/go/vet',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
@ -308,15 +280,16 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/build',
url: '/build',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
bottomGroup.resetOutput();
},
success: function (result) {
$("#buildRun").addClass("ico-stop")
.removeClass("ico-buildrun").attr("title", config.label.stop);
},
success: function (result) {
}
});
},
@ -340,7 +313,7 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/build',
url: '/build',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
@ -351,7 +324,7 @@ var menu = {
});
},
_initPreference: function () {
$("#dialogPreference").load(config.context + '/preference', function () {
$("#dialogPreference").load('/preference', function () {
$("#dialogPreference input").keyup(function () {
var isChange = false,
emptys = [],
@ -424,6 +397,9 @@ var menu = {
$fontFamily = $dialogPreference.find("input[name=fontFamily]"),
$fontSize = $dialogPreference.find("input[name=fontSize]"),
$goFmt = $dialogPreference.find("select[name=goFmt]"),
$GoBuildArgsForLinux = $dialogPreference.find("input[name=GoBuildArgsForLinux]"),
$GoBuildArgsForWindows = $dialogPreference.find("input[name=GoBuildArgsForWindows]"),
$GoBuildArgsForDarwin = $dialogPreference.find("input[name=GoBuildArgsForDarwin]"),
$workspace = $dialogPreference.find("input[name=workspace]"),
$password = $dialogPreference.find("input[name=password]"),
$email = $dialogPreference.find("input[name=email]"),
@ -440,9 +416,11 @@ var menu = {
"fontFamily": $fontFamily.val(),
"fontSize": $fontSize.val(),
"goFmt": $goFmt.val(),
"GoBuildArgsForLinux": $GoBuildArgsForLinux.val(),
"GoBuildArgsForWindows": $GoBuildArgsForWindows.val(),
"GoBuildArgsForDarwin": $GoBuildArgsForDarwin.val(),
"workspace": $workspace.val(),
"password": $password.val(),
"email": $email.val(),
"locale": $locale.val(),
"theme": $theme.val(),
"editorFontFamily": $editorFontFamily.val(),
@ -459,16 +437,19 @@ var menu = {
$.ajax({
type: 'POST',
url: config.context + '/preference',
url: '/preference',
data: JSON.stringify(request),
success: function (result, textStatus, jqXHR) {
if (!result.succ) {
if (0 != result.code) {
return false;
}
$fontFamily.data("value", $fontFamily.val());
$fontSize.data("value", $fontSize.val());
$goFmt.data("value", $goFmt.val());
$GoBuildArgsForLinux.data("value", $GoBuildArgsForLinux.val());
$GoBuildArgsForWindows.data("value", $GoBuildArgsForWindows.val());
$GoBuildArgsForDarwin.data("value", $GoBuildArgsForDarwin.val());
$workspace.data("value", $workspace.val());
$password.data("value", $password.val());
$email.data("value", $email.val());
@ -487,7 +468,7 @@ var menu = {
var $okBtn = $("#dialogPreference").closest(".dialog-main").find(".dialog-footer > button:eq(0)");
$okBtn.prop("disabled", true);
$("#themesLink").attr("href", config.staticServer + '/static/css/themes/' + $theme.val() + '.css');
$("#themesLink").attr("href", '/static/css/themes/' + $theme.val() + '.css');
config.editorTheme = $editorTheme.val();
for (var i = 0, ii = editors.data.length; i < ii; i++) {

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -19,7 +19,7 @@
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Dec 8, 2015
* @version 1.0.0.2, Jun 23, 2019
*/
var notification = {
init: function () {
@ -35,7 +35,7 @@ var notification = {
var notificationWS = new ReconnectingWebSocket(config.channel + '/notification/ws?sid=' + config.wideSessionId);
notificationWS.onopen = function () {
console.log('[notification onopen] connected');
// console.log('[notification onopen] connected');
};
notificationWS.onmessage = function (e) {
@ -44,7 +44,7 @@ var notification = {
notificationHTML = '';
if (data.cmd && "init-notification" === data.cmd) {
console.log('[notification onmessage]' + e.data);
// console.log('[notification onmessage]' + e.data);
return;
}
@ -58,11 +58,11 @@ var notification = {
};
notificationWS.onclose = function (e) {
console.log('[notification onclose] disconnected (' + e.code + ')');
// console.log('[notification onclose] disconnected (' + e.code + ')');
};
notificationWS.onerror = function (e) {
console.log('[notification onerror]');
console.log('[notification onerror]', e);
};
}
};

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -19,23 +19,14 @@
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Dec 8, 2015
* @version 1.0.0.3, Jun 23, 2019
*/
var playground = {
autocompleteMutex: false,
editor: undefined,
pid: undefined,
_resize: function () {
if (config.disqus) {
return false;
}
if (config.embed) {
$("#editorDiv").parent().height($(window).height() - 33 - $(".footer").height());
playground.editor.setSize("auto", ($("#editorDiv").parent().height() * 0.7) + "px");
} else {
$("#editor, #output").height($(window).height() - 60 - $(".footer").height());
playground.editor.setSize("auto", $("#editor").height() + "px");
}
},
_initShare: function () {
$("#dialogShare").dialog({
@ -61,8 +52,7 @@ var playground = {
var title = encodeURIComponent($('title').text() + '. \n' + $('meta[name=description]').attr('content')
+ " #golang#");
urls.weibo = "http://v.t.sina.com.cn/share/share.php?title=" + title + "&url=" + url + "&pic=" + pic;
urls.tencent = "http://share.v.t.qq.com/index.php?c=share&a=index&title=" + title +
"&url=" + url + "&pic=" + pic;
urls.qqz = "https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=" + url + "&sharesource=qzone&title=" + title+ "&pics=" + pic;
window.open(urls[key], "_blank", "top=100,left=200,width=648,height=618");
@ -91,7 +81,6 @@ var playground = {
var autocompleteHints = [];
if (playground.autocompleteMutex && editor.state.completionActive) {
console.log(1);
return;
}
@ -100,7 +89,7 @@ var playground = {
$.ajax({
async: false, // 同步执行
type: 'POST',
url: config.context + '/playground/autocomplete',
url: '/playground/autocomplete',
data: JSON.stringify(request),
dataType: "json",
success: function (data) {
@ -210,11 +199,8 @@ var playground = {
playground._resize();
});
if (config.embed) {
playground.editor.setSize("auto", ($("#editorDiv").parent().height() * 0.7) + "px");
} else {
playground.editor.setSize("auto", $("#editor").height() + "px");
}
var hovered = false;
$(".menu .ico-share").hover(function () {
@ -280,34 +266,33 @@ var playground = {
this._initWideShare();
this._initShare();
menu._initAbout();
this._initGoNews();
},
_initWS: function () {
// Used for session retention, server will release all resources of the session if this channel closed
var sessionWS = new ReconnectingWebSocket(config.channel + '/session/ws?sid=' + config.wideSessionId);
sessionWS.onopen = function () {
console.log('[session onopen] connected');
// console.log('[session onopen] connected');
};
sessionWS.onmessage = function (e) {
console.log('[session onmessage]' + e.data);
// console.log('[session onmessage]' + e.data);
};
sessionWS.onclose = function (e) {
console.log('[session onclose] disconnected (' + e.code + ')');
// console.log('[session onclose] disconnected (' + e.code + ')');
};
sessionWS.onerror = function (e) {
console.log('[session onerror] ' + JSON.parse(e));
// console.log('[session onerror] ' + JSON.parse(e));
};
var playgroundWS = new ReconnectingWebSocket(config.channel + '/playground/ws?sid=' + config.wideSessionId);
playgroundWS.onopen = function () {
console.log('[playground onopen] connected');
// console.log('[playground onopen] connected');
};
playgroundWS.onmessage = function (e) {
console.log('[playground onmessage]' + e.data);
var data = JSON.parse(e.data);
if ("init-playground" === data.cmd) {
@ -316,16 +301,56 @@ var playground = {
playground.pid = data.pid;
var val = $("#output").val();
$("#output").val(val + data.output);
var output = $("#output").html();
if ("" === output) {
output = "<pre>" + data.output + "</pre>";
} else {
output = output.replace(/<\/pre>$/g, data.output + '</pre>');
}
output = output.replace(/\r/g, '');
output = output.replace(/\n/g, '<br/>');
if (-1 !== output.indexOf("<br/>")) {
output = Autolinker.link(output);
}
$("#output").html(output);
};
playgroundWS.onclose = function (e) {
console.log('[playground onclose] disconnected (' + e.code + ')');
// console.log('[playground onclose] disconnected (' + e.code + ')');
};
playgroundWS.onerror = function (e) {
console.log('[playground onerror] ' + JSON.parse(e));
console.log('[playground onerror] ', e);
};
},
_initGoNews: function () {
$.ajax({
url: "https://ld246.com/apis/articles?tags=wide,golang&p=1&size=20",
type: "GET",
dataType: "jsonp",
jsonp: "callback",
success: function (data, textStatus) {
var articles = data.articles;
if (0 === articles.length) {
return;
}
var length = articles.length;
var listHTML = "<ul><li class='title'>" + config.label.community +
"<a href='https://ld246.com/article/1437497122181' target='_blank' class='fn-right'>边看边练</li>";
for (var i = 0; i < length; i++) {
var article = articles[i];
listHTML += "<li>"
+ "<a target='_blank' href='"
+ article.articlePermalink + "'>"
+ article.articleTitle + "</a>"
+"</span></li>";
}
$("#goNews").html(listHTML + "</ul>");
}
});
},
share: function () {
if (!playground.editor) {
return;
@ -338,7 +363,7 @@ var playground = {
$.ajax({
type: 'POST',
url: config.context + '/playground/save',
url: '/playground/save',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
@ -346,8 +371,7 @@ var playground = {
playground.editor.setValue(data.code);
if (!result.succ) {
console.log(data);
if (0 != result.code) {
return;
}
@ -355,49 +379,19 @@ var playground = {
var request = newWideRequest();
request.url = url;
$.ajax({
type: 'POST',
url: config.context + '/playground/short-url',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
console.log(result);
return;
}
var html = '<div class="fn-clear"><label>' + config.label.url
+ config.label.colon + '</label><a href="'
+ url + '" target="_blank">' + url + "</a><br/>";
html += '<label>' + config.label.short_url + config.label.colon
+ '</label><a href="' + result.data + '" target="_blank">'
+ result.data + '</a><br/>';
html += '<label>' + config.label.embeded + config.label.colon
+ '</label><br/><textarea rows="5" style="width:100%" readonly><iframe style="border:1px solid" src="'
+ url + '?embed=true" width="99%" height="600"></iframe></textarea>';
+ url + '" width="99%" height="600"></iframe></textarea>';
html += '</div>';
$("#dialogShare").html(html);
$("#dialogShare").dialog("open");
}});
}
});
},
disqus: function () {
var url = window.location.href;
if (url.indexOf("?") >= 0) {
if (url.indexOf("disqus=") >= 0) {
url = url.replace("disqus=false", "disqus=true");
console.log(url);
} else {
url += "&disqus=true";
}
} else {
url += "?disqus=true";
}
window.location.href = url;
},
stop: function () {
if (!playground.editor) {
return;
@ -417,7 +411,7 @@ var playground = {
$.ajax({
type: 'POST',
url: config.context + '/playground/stop',
url: '/playground/stop',
data: JSON.stringify(request),
dataType: "json"
});
@ -436,11 +430,11 @@ var playground = {
var request = newWideRequest();
request.code = code;
$("#output").val("");
$("#output").html("");
$.ajax({
type: 'POST',
url: config.context + '/playground/save',
url: '/playground/save',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
@ -449,7 +443,7 @@ var playground = {
playground.editor.setValue(data.code);
playground.editor.setCursor(cursor);
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -459,17 +453,15 @@ var playground = {
$.ajax({
type: 'POST',
url: config.context + '/playground/build',
url: '/playground/build',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
console.log(result);
var data = result.data;
$("#output").val(data.output);
$("#output").html(data.output);
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -479,7 +471,7 @@ var playground = {
$.ajax({
type: 'POST',
url: config.context + '/playground/run',
url: '/playground/run',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
@ -506,7 +498,7 @@ var playground = {
$.ajax({
type: 'POST',
url: config.context + '/playground/save',
url: '/playground/save',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -18,7 +18,7 @@
* @file session.js
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @version 1.1.0.1, Dec 8, 2015
* @version 1.1.0.2, Jun 23, 2019
*/
var session = {
init: function () {
@ -74,7 +74,7 @@ var session = {
$.ajax({
type: 'POST',
url: config.context + '/session/save',
url: '/session/save',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
@ -158,8 +158,6 @@ var session = {
var sessionWS = new ReconnectingWebSocket(config.channel + '/session/ws?sid=' + config.wideSessionId);
sessionWS.onopen = function () {
console.log('[session onopen] connected');
var dateFormat = function (time, fmt) {
var date = new Date(time);
var dateObj = {
@ -238,7 +236,7 @@ var session = {
}
};
sessionWS.onclose = function (e) {
console.log('[session onclose] disconnected (' + e.code + ')');
// console.log('[session onclose] disconnected (' + e.code + ')');
var data = {type: "Network", severity: "ERROR",
message: "Disconnected from server, trying to reconnect it [sid=" + config.wideSessionId + "]"},
@ -253,7 +251,7 @@ var session = {
$(".notification-count").show();
};
sessionWS.onerror = function (e) {
console.log('[session onerror]');
console.log('[session onerror]', e);
};
}
};

View File

@ -1,61 +0,0 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @file shell.js
*
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.1, Dec 8, 2015
*/
var shell = {
_shellWS: undefined,
_initWS: function () {
shell.shellWS = new ReconnectingWebSocket(config.channel + '/shell/ws?sid=' + config.wideSessionId);
shell.shellWS.onopen = function () {
console.log('[shell onopen] connected');
};
shell.shellWS.onmessage = function (e) {
console.log('[shell onmessage]' + e.data);
var data = JSON.parse(e.data);
if ('init-shell' !== data.cmd) {
$('#shellOutput').val(data.output);
}
};
shell.shellWS.onclose = function (e) {
console.log('[shell onclose] disconnected (' + e.code + ')');
};
shell.shellWS.onerror = function (e) {
console.log('[shell onerror] ' + e);
};
},
init: function () {
this._initWS();
$('#shellInput').keydown(function (event) {
if (13 === event.which) {
var input = {
cmd: $('#shellInput').val()
};
shell.shellWS.send(JSON.stringify(input));
$('#shellInput').val('');
}
});
}
};
$(document).ready(function () {
shell.init();
});

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,

View File

@ -1,11 +1,11 @@
/*
* Copyright (c) 2014-2016, b3log.org & hacpai.com
* Copyright (c) 2014-present, b3log.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@ -160,11 +160,11 @@ var tree = {
$.ajax({
async: false,
type: 'POST',
url: config.context + '/file/zip/new',
url: '/file/zip/new',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -175,7 +175,7 @@ var tree = {
});
if (isSucc) {
window.open(config.context + '/file/zip?path=' + wide.curNode.path + ".zip");
window.open('/file/zip?path=' + wide.curNode.path + ".zip");
}
},
crossCompile: function (platform) {
@ -186,11 +186,11 @@ var tree = {
$.ajax({
async: false,
type: 'POST',
url: config.context + '/cross',
url: '/cross',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -198,28 +198,6 @@ var tree = {
}
});
},
decompress: function () {
var request = newWideRequest();
request.path = wide.curNode.path;
$.ajax({
async: false,
type: 'POST',
url: config.context + '/file/decompress',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
$("#dialogAlert").dialog("open", result.msg);
return false;
}
var dir = wide.curNode.getParentNode();
tree.fileTree.reAsyncChildNodes(dir, "refresh");
}
});
},
refresh: function (it) {
if (it) {
if ($(it).hasClass("disabled")) {
@ -229,31 +207,6 @@ var tree = {
tree.fileTree.reAsyncChildNodes(wide.curNode, "refresh", true);
},
gitClone: function (it) {
if (it) {
if ($(it).hasClass("disabled")) {
return false;
}
}
$("#dialogGitClonePrompt").dialog('open');
},
import: function () {
var request = newWideRequest();
request.path = wide.curNode.path;
$('#importFileupload').fileupload({
url: "/file/upload?path=" + request.path,
dataType: 'json',
formData: request,
done: function (e, result) {
tree.fileTree.reAsyncChildNodes(wide.curNode, "refresh");
},
fail: function () {
console.log(arguments);
}
});
},
init: function () {
$("#file").click(function () {
$(this).focus();
@ -263,11 +216,11 @@ var tree = {
$.ajax({
type: 'POST',
url: config.context + '/files',
url: '/files',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
var $dirRMenu = $("#dirRMenu");
var $fileRMenu = $("#fileRMenu");
var setting = {
@ -282,7 +235,7 @@ var tree = {
},
async: {
enable: true,
url: config.context + "/file/refresh",
url: "/file/refresh",
autoParam: ["path"]
},
callback: {
@ -417,11 +370,11 @@ var tree = {
$.ajax({
async: false,
type: 'POST',
url: config.context + '/file',
url: '/file',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -444,7 +397,7 @@ var tree = {
if ("img" === data.mode) { // 是图片文件的话新建 tab 打开
// 最好是开 tab但这个最终取决于浏览器设置
var w = window.open(config.context + data.path);
var w = window.open(data.path);
return false;
}
@ -506,11 +459,11 @@ var tree = {
$.ajax({
type: 'POST',
url: config.context + '/file/search/text',
url: '/file/search/text',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -542,11 +495,11 @@ var tree = {
$.ajax({
type: 'POST',
url: config.context + '/file/rename',
url: '/file/rename',
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogRenamePrompt").dialog("close");
bottomGroup.tabs.setCurrent("notification");
windows.flowBottom();

Some files were not shown because too many files have changed in this diff Show More