Compare commits

...

97 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
95 changed files with 1266 additions and 5028 deletions

2
.gitattributes vendored
View File

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

View File

@ -1,46 +0,0 @@
---
name: 报告缺陷
about: 报告缺陷以帮助我们改进
---
**请先看[《提问的智慧》](https://hacpai.com/article/1536377163156)**,并尝试到[讨论区](https://hacpai.com/tag/wide)搜寻资料解决问题。
----
### 描述问题
请尽量清晰精准地描述你碰到的问题。
### 重现步骤
请描述如何重现这个问题:
1. Go to '...'
2. Click on '...'
3. Scroll down to '...'
4. See error
### 期待的结果
请尽量清晰精准地描述你所期待的结果。
### 截屏或录像
如果可能,请尽量附加截图或录像来描述你遇到的问题。
### 桌面端环境
- OS: [e.g. windows, mac, linux]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
### 移动端环境
- Device: [e.g. iPhone8]
- OS: [e.g. iOS12]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
### 其他信息
请提供其他附加信息帮助我们诊断问题。

View File

@ -1,20 +0,0 @@
---
name: 请求新功能
about: 提出你期待的功能特性
---
### 你在什么场景下需要该功能?
请尽量清晰精准地描述你碰到的问题。
### 描述可能的解决方案
请尽量清晰精准地描述你期待我们要做的,描述你想到的实现方案。
### 描述你认为的候选方案
请尽量清晰精准地描述你能接受的候选解决方案。
### 其他信息
请提供关于该功能建议的其他附加信息。

View File

@ -2,10 +2,3 @@ language: go
go:
- 1.12
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

146
README.md
View File

@ -1,160 +1,18 @@
<p align = "center">
<img alt="Wide" src="https://user-images.githubusercontent.com/873584/57901570-5355ba00-7898-11e9-96ca-45b75b1d70db.png">
<br><br>
一款基于 Web 的 Go 语言 IDE
<br><br>
<a title="Build Status" target="_blank" href="https://travis-ci.org/b3log/wide"><img src="https://img.shields.io/travis/b3log/wide.svg?style=flat-square"></a>
<a title="Go Report Card" target="_blank" href="https://goreportcard.com/report/github.com/b3log/wide"><img src="https://goreportcard.com/badge/github.com/b3log/wide?style=flat-square"></a>
<a title="Coverage Status" target="_blank" href="https://coveralls.io/repos/github/b3log/wide/badge.svg?branch=master"><img src="https://img.shields.io/coveralls/github/b3log/wide.svg?style=flat-square&color=CC9933"></a>
<a title="Code Size" target="_blank" href="https://github.com/b3log/wide"><img src="https://img.shields.io/github/languages/code-size/b3log/wide.svg?style=flat-square"></a>
<a title="Apache License" target="_blank" href="https://github.com/b3log/wide/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-apache2-orange.svg?style=flat-square"></a>
<br>
<a title="Releases" target="_blank" href="https://github.com/b3log/wide/releases"><img src="https://img.shields.io/github/release/b3log/wide.svg?style=flat-square"></a>
<a title="Release Date" target="_blank" href="https://github.com/b3log/wide/releases"><img src="https://img.shields.io/github/release-date/b3log/wide.svg?style=flat-square&color=99CCFF"></a>
<a title="GitHub Commits" target="_blank" href="https://github.com/b3log/wide/commits/master"><img src="https://img.shields.io/github/commit-activity/m/b3log/wide.svg?style=flat-square"></a>
<a title="Last Commit" target="_blank" href="https://github.com/b3log/wide/commits/master"><img src="https://img.shields.io/github/last-commit/b3log/wide.svg?style=flat-square&color=FF9900"></a>
<a title="GitHub Pull Requests" target="_blank" href="https://github.com/b3log/wide/pulls"><img src="https://img.shields.io/github/issues-pr-closed/b3log/wide.svg?style=flat-square&color=FF9966"></a>
<a title="Hits" target="_blank" href="https://github.com/b3log/hits"><img src="https://hits.b3log.org/b3log/wide.svg"></a>
<br><br>
<a title="GitHub Watchers" target="_blank" href="https://github.com/b3log/wide/watchers"><img src="https://img.shields.io/github/watchers/b3log/wide.svg?label=Watchers&style=social"></a>&nbsp;&nbsp;
<a title="GitHub Stars" target="_blank" href="https://github.com/b3log/wide/stargazers"><img src="https://img.shields.io/github/stars/b3log/wide.svg?label=Stars&style=social"></a>&nbsp;&nbsp;
<a title="GitHub Forks" target="_blank" href="https://github.com/b3log/wide/network/members"><img src="https://img.shields.io/github/forks/b3log/wide.svg?label=Forks&style=social"></a>&nbsp;&nbsp;
<a title="Author GitHub Followers" target="_blank" href="https://github.com/88250"><img src="https://img.shields.io/github/followers/88250.svg?label=Followers&style=social"></a>
</p>
## 简介
Wide 是一款基于 **W**eb 的 Go 语言 **IDE**
## 动机
目前较为流行的 Go IDE 都有一些缺陷或遗憾:
* 文本编辑器类vim/emacs/sublime/Atom 等):对于新手门槛太高,搭建复杂
* 插件类goclipse、IDEA 等):需要原 IDE 支持,不够专业
* LiteIDE 界面不够 modern、goland 收费
* **缺少网络分享、嵌入网站可运行功能**
另外Go IDE 很少,用 Go 本身开发的 IDE 更是没有,这是一次很好的尝试。关于产品定位的讨论请看[这里](https://hacpai.com/article/1438407961481)。
## 特性
基于 Web 的 IDE
* 只需要浏览器就能进行开发、运行
* 跨平台,甚至在移动设备上
* 易进行功能扩展
* 易与其他系统集成
* 极客体验
核心功能:
* 代码高亮、折叠Go/HTML/JavaScript/Markdown 等
* 自动完成Go/HTML 等
* 编译检查:编辑器提示编译错误
* 格式化Go/HTML/JSON 等
* 运行:支持同时运行多个程序
* 代码导航:跳转到声明,查找使用,文件搜索等
* Web 开发前端HTML/JS/CSS开发支持
* go toolgo mod/install/fmt 等
* 项目文件导出
* UI/编辑器多主题
* 支持交叉编译
## 界面
### 主界面
* [Wide 用户指南](https://ld246.com/article/1538873544275)
* [Wide 开发指南](https://ld246.com/article/1538876422995)
![Overview](https://cloud.githubusercontent.com/assets/873584/5450620/1d51831e-8543-11e4-930b-670871902425.png)
### 跳转到文件
![Goto File](https://cloud.githubusercontent.com/assets/873584/5450616/1d495da6-8543-11e4-9285-f9d9c60779ac.png)
### 自动完成
![Autocomplete](https://cloud.githubusercontent.com/assets/873584/5450619/1d4d5712-8543-11e4-8fe4-35dbc8348a6e.png)
### 主题
![Theme](https://cloud.githubusercontent.com/assets/873584/5450617/1d4c0826-8543-11e4-8b86-f79a4e41550a.png)
### 查看表达式
![Show Expression Info](https://cloud.githubusercontent.com/assets/873584/5450618/1d4cd9f4-8543-11e4-950f-121bd3ff4a39.png)
### 构建报错提示
![Build Error Info](https://cloud.githubusercontent.com/assets/873584/5450632/3e51cccc-8543-11e4-8ca8-8d2427aa16b8.png)
### 交叉编译
![Cross-Compilation](https://cloud.githubusercontent.com/assets/873584/10130037/226d75fc-65f7-11e5-94e4-25ee579ca175.png)
### Playground
![Playground](https://cloud.githubusercontent.com/assets/873584/21209772/449ecfd2-c2b1-11e6-9aa6-a83477d9f269.gif)
## 架构
### 构建与运行
![Build & Run](https://cloud.githubusercontent.com/assets/873584/4389219/3642bc62-43f3-11e4-8d1f-06d7aaf22784.png)
* 一个浏览器 tab 对应一个 Wide 会话
* 通过 WebSocket 进行程序执行输出推送
1. 客户端浏览器发送 ````Build```` 请求
2. 服务器使用 ````os/exec```` 执行 ````go build```` 命令<br/>
2.1. 生成可执行文件
3. 客户端浏览器发送 ````Run```` 请求
4. 服务器使用 ````os/exec```` 执行文件<br/>
4.1. 生成进程<br/>
4.2. 运行结果输出到 WebSocket 通道
5. 客户端浏览器监听 ````ws.onmessage```` 到消息后做展现
### 代码辅助
![](https://cloud.githubusercontent.com/assets/873584/4399135/3b80c21c-4463-11e4-8e94-7f7e8d12a4df.png)
* 自动完成
* 查找使用
1. 浏览器客户端发送代码辅助请求
2. Handler 根据请求对应的 HTTP 会话获取用户工作空间
3. 执行 `gocode`/`ide_stub(gotools)` 命令<br/>
3.1 设置环境变量(${GOPATH} 为用户工作空间路径)<br/>
3.2 `gocode` 命令需要设置参数 `lib-path`
## 文档
* [用户指南](https://hacpai.com/article/1538873544275)
* [开发指南](https://hacpai.com/article/1538876422995)
## 社区
* [讨论区](https://hacpai.com/tag/wide)
* [报告问题](https://github.com/b3log/wide/issues/new/choose)
## 授权
Wide 使用 [Apache License, Version 2](https://www.apache.org/licenses/LICENSE-2.0) 作为开源协议,请务必遵循该开源协议相关约定。
## 鸣谢
* [golang](https://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)
----
## 开源项目推荐
* 如果你需要搭建一个个人博客系统,可以考虑使用 [Solo](https://github.com/b3log/solo)
* 如果你需要搭建一个多用户博客平台,可以考虑使用 [Pipe](https://github.com/b3log/pipe)
* 如果你需要搭建一个社区平台,可以考虑使用 [Sym](https://github.com/b3log/symphony)
* 欢迎加入我们的小众开源社区,详情请看[这里](https://hacpai.com/article/1463025124998)

View File

@ -107,7 +107,7 @@ func NewUser(id, name, avatar, workspace string) *User {
return &User{Id: id, Name: name, Avatar: avatar, Workspace: workspace,
Locale: Wide.Locale, GoFormat: "gofmt",
GoBuildArgsForLinux: "-i", GoBuildArgsForWindows: "-i", GoBuildArgsForDarwin: "-i",
GoBuildArgsForLinux: "", GoBuildArgsForWindows: "", GoBuildArgsForDarwin: "",
FontFamily: "Helvetica", FontSize: "13px", Theme: "default",
Keymap: "wide",
Created: now, Updated: now, Lived: now,

View File

@ -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,18 +37,18 @@ const (
PathListSeparator = string(os.PathListSeparator)
// WideVersion holds the current Wide's version.
WideVersion = "1.5.3"
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/b3log/wide"
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")
}
`
)
@ -65,10 +63,17 @@ type conf struct {
StaticResourceVersion string // version of static resources
Locale string // default locale
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
@ -83,18 +88,14 @@ var Docker bool
const DockerImageGo = "golang"
// Load loads the Wide configurations from wide.json and users' configurations from users/{userId}.json.
func Load(confPath, confData, confServer, confLogLevel string) {
initWide(confPath, confData, confServer, confLogLevel)
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 {
if !util.OS.IsWindows() {
logger.Errorf("Not found 'docker' installed, running user's code will cause security problem")
os.Exit(-1)
}
logger.Warnf("Not found 'docker' installed, running user's code will cause security problem")
} else {
Docker = true
}
@ -127,7 +128,7 @@ func initUsers() {
user := &User{}
bytes, _ := ioutil.ReadFile(filepath.Join(Wide.Data, "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)
@ -141,15 +142,15 @@ func initUsers() {
}
// 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"
}
//if "" == user.GoBuildArgsForLinux {
// user.GoBuildArgsForLinux = "-i"
//}
//if "" == user.GoBuildArgsForWindows {
// user.GoBuildArgsForWindows = "-i"
//}
//if "" == user.GoBuildArgsForDarwin {
// user.GoBuildArgsForDarwin = "-i"
//}
Users = append(Users, user)
}
@ -158,8 +159,8 @@ func initUsers() {
initCustomizedConfs()
}
func initWide(confPath, confData, confServer, confLogLevel string) {
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)
@ -175,17 +176,19 @@ func initWide(confPath, confData, confServer, confLogLevel string) {
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))
// 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)
@ -220,6 +223,15 @@ func initWide(confPath, confData, confServer, confLogLevel string) {
Wide.Server = confServer
}
if "" != confReadOnly {
Wide.ReadOnly, _ = strconv.ParseBool(confReadOnly)
}
// 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)
@ -233,14 +245,14 @@ func FixedTimeCheckEnv() {
checkEnv() // check immediately
go func() {
for _ = range time.Tick(time.Minute*7) {
for _ = range time.Tick(time.Minute * 7) {
checkEnv()
}
}()
}
func checkEnv() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
cmd := exec.Command("go", "version")
buf, err := cmd.CombinedOutput()
@ -257,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 {
@ -295,7 +307,7 @@ func GetGoFmt(userId string) string {
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"
@ -405,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

@ -2,9 +2,15 @@
"Server": "http://127.0.0.1:7070",
"LogLevel": "debug",
"Data": "${home}/wide",
"RuntimeMode": "dev",
"RuntimeMode": "prod",
"HTTPSessionMaxAge": 86400,
"StaticResourceVersion": "${time}",
"Locale": "zh_CN",
"Autocomplete": true
"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 '*/_*' -not -path './vendor*' -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

View File

@ -25,85 +25,24 @@ 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, session.CookieName)
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(), http.StatusInternalServerError)
@ -120,9 +59,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
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(), http.StatusInternalServerError)
@ -132,7 +69,6 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
code := args["code"].(string)
fout.WriteString(code)
if err := fout.Close(); nil != err {
logger.Error(err)
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -142,9 +78,7 @@ func AutocompleteHandler(w http.ResponseWriter, r *http.Request) {
line := int(args["cursorLine"].(float64))
ch := int(args["cursorCh"].(float64))
offset := getCursorOffset(code, line, ch)
logger.Tracef("offset: %d", offset)
userWorkspace := conf.GetUserWorkspace(uid)
@ -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)}
@ -179,8 +113,8 @@ 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, session.CookieName)
uid := session.Values["uid"].(string)
@ -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,7 +157,7 @@ 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
@ -240,7 +174,7 @@ func GetExprInfoHandler(w http.ResponseWriter, r *http.Request) {
exprInfo := strings.TrimSpace(string(output))
if "" == exprInfo {
result.Succ = false
result.Code = -1
return
}
@ -250,8 +184,8 @@ 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, session.CookieName)
if session.IsNew {
@ -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,7 +233,7 @@ 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
@ -316,7 +250,7 @@ func FindDeclarationHandler(w http.ResponseWriter, r *http.Request) {
found := strings.TrimSpace(string(output))
if "" == found {
result.Succ = false
result.Code = -1
return
}
@ -338,8 +272,8 @@ 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, session.CookieName)
if session.IsNew {
@ -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,7 +321,7 @@ 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
@ -404,7 +338,7 @@ func FindUsagesHandler(w http.ResponseWriter, r *http.Request) {
out := strings.TrimSpace(string(output))
if "" == out {
result.Succ = false
result.Code = -1
return
}

View File

@ -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,8 +31,14 @@ 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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
@ -46,15 +52,15 @@ func GoFmtHandler(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
}
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
}
@ -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

@ -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

@ -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

@ -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{}}
@ -82,8 +81,8 @@ func GetFilesHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid)
workspaces := filepath.SplitList(userWorkspace)
@ -134,7 +133,7 @@ func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
path := r.FormValue("path")
if !util.Go.IsAPI(path) && !session.CanAccess(uid, path) {
if !gulu.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -164,29 +163,29 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid, 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,7 +198,7 @@ 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"
@ -221,8 +220,8 @@ func GetFileHandler(w http.ResponseWriter, r *http.Request) {
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
@ -240,14 +239,20 @@ func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid, 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,
@ -296,21 +301,21 @@ func NewFileHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid, 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}
@ -348,21 +353,21 @@ func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid, 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}
@ -394,20 +399,20 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
}
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) ||
if gulu.Go.IsAPI(oldPath) ||
!session.CanAccess(uid, oldPath) {
http.Error(w, "Forbidden", http.StatusForbidden)
@ -415,7 +420,7 @@ func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
}
newPath := args["newPath"].(string)
if util.Go.IsAPI(newPath) || !session.CanAccess(uid, 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}
@ -459,19 +464,19 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
}
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(uid, path) {
if !gulu.Go.IsAPI(path) && !session.CanAccess(uid, path) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -482,7 +487,7 @@ func FindHandler(w http.ResponseWriter, r *http.Request) {
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)})
}
@ -512,14 +517,14 @@ func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
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
}
@ -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

@ -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
}

36
go.mod
View File

@ -1,21 +1,23 @@
module github.com/b3log/wide
module github.com/88250/wide
go 1.12
go 1.20
require (
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f // indirect
github.com/fsnotify/fsnotify v1.4.7
github.com/gorilla/context v0.0.0-20141217160251-215affda49ad // indirect
github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 // indirect
github.com/gorilla/sessions v0.0.0-20150417174705-f61c3ec2cf65
github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a
github.com/hashicorp/go-version v1.2.0
github.com/moul/http2curl v1.0.0 // indirect
github.com/parnurzeal/gorequest v0.2.15
github.com/pkg/errors v0.8.1 // indirect
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
golang.org/x/net v0.0.0-20190514140710-3ec191127204 // indirect
golang.org/x/sys v0.0.0-20190515190549-87c872767d25 // indirect
golang.org/x/text v0.3.2
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
)

57
go.sum
View File

@ -1,43 +1,44 @@
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f h1:8GDPb0tCY8LQ+OJ3dbHb5sA6YZWXFORQYZx5sdsTlMs=
github.com/elazarl/goproxy v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f h1:AUj1VoZUfhPhOPHULCQQDnGhRelpFWHMLhQVWDsS0v4=
github.com/elazarl/goproxy/ext v0.0.0-20190421051319-9d40249d3c2f/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
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/context v0.0.0-20141217160251-215affda49ad h1:wJwKN6X6iRRVnjdBgrkWjhBOvYm7yw5boqXwFUnBtbE=
github.com/gorilla/context v0.0.0-20141217160251-215affda49ad/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4 h1:X0DbEdoaUJT+NZ8mLHRNMSLmogzqLhsA1Eh6gs7Y7Zg=
github.com/gorilla/securecookie v0.0.0-20150327155805-8e98dd730fc4/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v0.0.0-20150417174705-f61c3ec2cf65 h1:bDPV+Nh80WIp0U0a/o9wKilWRxH+l8jIySjElU91LeA=
github.com/gorilla/sessions v0.0.0-20150417174705-f61c3ec2cf65/go.mod h1:+WVp8kdw6VhyKExm03PAMRn2ZxnPtm58pV0dBVPdhHE=
github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a h1:p/PGT+3UGSK7eULOGHUMCUxI5U976R34HWuKHbFpK3Q=
github.com/gorilla/websocket v0.0.0-20150530030352-a3ec486e6a7a/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E=
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
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/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/parnurzeal/gorequest v0.2.15 h1:oPjDCsF5IkD4gUk6vIgsxYNaSgvAnIh1EJeROn3HdJU=
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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 v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
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-20190514140710-3ec191127204 h1:4yG6GqBtw9C+UrLp6s2wtSniayy/Vd/3F7ffLE427XI=
golang.org/x/net v0.0.0-20190514140710-3ec191127204/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-20190515190549-87c872767d25 h1:SSKQq5sjDoW0L0NaDoVl7d7HmtTxM0ezm0Ef9azs4uQ=
golang.org/x/sys v0.0.0-20190515190549-87c872767d25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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

@ -22,7 +22,7 @@
*/
var gulp = require('gulp')
var concat = require('gulp-concat')
var minifyCSS = require('gulp-minify-css')
var cleanCSS = require('gulp-clean-css')
var uglify = require('gulp-uglify')
var sourcemaps = require('gulp-sourcemaps')
@ -37,14 +37,14 @@ function minLibCSS () {
'./static/js/lib/codemirror-5.1/addon/dialog/dialog.css',
'./static/js/overwrite/codemirror/theme/*.css']
return gulp.src(cssLibs).
pipe(minifyCSS()).
pipe(cleanCSS()).
pipe(concat('lib.min.css')).
pipe(gulp.dest('./static/css/'))
}
function minZTreeStyleCSS () {
return gulp.src('./static/js/lib/ztree/zTreeStyle.css').
pipe(minifyCSS()).
pipe(cleanCSS()).
pipe(concat('zTreeStyle.min.css')).
pipe(gulp.dest('./static/js/lib/ztree/'))
}
@ -60,7 +60,7 @@ function minWideCSS () {
]
return gulp.src(cssWide).
pipe(minifyCSS()).
pipe(cleanCSS()).
pipe(concat('wide.min.css')).
pipe(gulp.dest('./static/css/'))
}

View File

@ -28,7 +28,6 @@
"close_all_files": "Close All",
"save_all_files": "Save All",
"format": "Format",
"gomod": "go mod",
"goinstall": "go install",
"build": "Build",
"build_n_run": "Build&Run",
@ -102,9 +101,6 @@
"start-install": "START [go install]",
"install-succ": "[go install] SUCCESS",
"install-error": "[go install] ERROR",
"start-mod": "START [go mod]",
"mod-succ": "[go mod] SUCCESS",
"mod-error": "[go mod] ERROR",
"check_version": "Checking update",
"new_version_available": "new version available",
"go_env": "Go",
@ -170,5 +166,6 @@
"download": "Download",
"decompress": "Decompress",
"keymap": "Keymap",
"resize": "Resize"
"resize": "Resize",
"sponsor": "Sponsor"
}

View File

@ -28,7 +28,6 @@
"close_all_files": "全てのファイルを閉じる",
"save_all_files": "全てを保存",
"format": "フォーマット",
"gomod": "go mod",
"goinstall": "go install",
"build": "ビルド",
"build_n_run": "ビルド実行",
@ -102,9 +101,6 @@
"start-install": "[go install] 開始",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失敗",
"start-mod": "[go mod] 開始",
"mod-succ": "[go mod] 成功",
"mod-error": "[go mod] 失敗",
"check_version": "更新をチェック中",
"new_version_available": "新しいバージョンがあります",
"go_env": "Go",
@ -159,9 +155,9 @@
"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": "リンク",
@ -170,5 +166,6 @@
"download": "ダウンロード",
"decompress": "解凍する",
"keymap": "キーマップ",
"resize": "サイズ変更"
"resize": "サイズ変更",
"sponsor": "スポンサー"
}

View File

@ -28,7 +28,6 @@
"close_all_files": "모두 닫기",
"save_all_files": "일괄저장",
"format": "포멧",
"gomod": "go mod",
"goinstall": "go install",
"build": "빌드",
"build_n_run": "빌드후실행",
@ -102,9 +101,6 @@
"start-install": "시작 [go install]",
"install-succ": "[go install] 성공",
"install-error": "[go install] 실패",
"start-mod": "시작 [go mod]",
"mod-succ": "[go mod] 성공",
"mod-error": "[go mod] 실패",
"check_version": "최신버전검색중",
"new_version_available": "최신업데이트 사용 가능",
"go_env": "Go 환경",
@ -159,9 +155,9 @@
"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": "하이퍼링크",
@ -170,5 +166,6 @@
"download": "다운로드",
"decompress": "압축풀기",
"keymap": "단축키",
"resize": "크기조절"
"resize": "크기조절",
"sponsor": "후원사"
}

View File

@ -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 {
@ -59,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

@ -28,7 +28,6 @@
"close_all_files": "关闭所有文件",
"save_all_files": "保存所有文件",
"format": "格式化",
"gomod": "go mod",
"goinstall": "go install",
"build": "构建",
"build_n_run": "构建并运行",
@ -102,9 +101,6 @@
"start-install": "开始 [go install]",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失败",
"start-mod": "开始 [go mod]",
"mod-succ": "[go mod] 成功",
"mod-error": "[go mod] 失败",
"check_version": "正在检查更新",
"new_version_available": "新版本可用",
"go_env": "Go 环境",
@ -159,9 +155,9 @@
"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": "链接",
@ -170,5 +166,6 @@
"download": "下载",
"decompress": "解压缩",
"keymap": "快捷键",
"resize": "调整大小"
"resize": "调整大小",
"sponsor": "赞助"
}

View File

@ -28,7 +28,6 @@
"close_all_files": "關閉所有檔案",
"save_all_files": "儲存所有檔案",
"format": "格式化",
"gomod": "go mod",
"goinstall": "go install",
"build": "編譯",
"build_n_run": "編譯並執行",
@ -102,9 +101,6 @@
"start-install": "開始 [go install]",
"install-succ": "[go install] 成功",
"install-error": "[go install] 失敗",
"start-mod": "開始 [go mod]",
"mod-succ": "[go mod] 成功",
"mod-error": "[go mod] 失敗",
"check_version": "正在檢查更新",
"new_version_available": "可用新版本",
"go_env": "Go 環境",
@ -170,5 +166,6 @@
"download": "下載",
"decompress": "解壓縮",
"keymap": "快速鍵",
"resize": "調整大小"
"resize": "調整大小",
"sponsor": "贊助"
}

View File

@ -1,217 +0,0 @@
// 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 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-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 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
}
}

52
main.go
View File

@ -29,21 +29,20 @@ import (
"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/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() {
@ -51,13 +50,15 @@ func init() {
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")
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()
//wd := gulu.OS.Pwd()
//if strings.HasPrefix(wd, os.TempDir()) {
// logger.Error("Don't run Wide in OS' temp directory or with `go run`")
//
@ -66,14 +67,14 @@ func init() {
i18n.Load()
event.Load()
conf.Load(*confPath, *confData, *confServer, *confLogLevel)
conf.Load(*confPath, *confData, *confServer, *confLogLevel, *confReadOnly, template.HTML(*confSiteStatCode))
conf.FixedTimeCheckEnv()
session.FixedTimeSave()
session.FixedTimeRelease()
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.
@ -93,8 +94,8 @@ func main() {
serveSingle("/favicon.ico", "./static/images/favicon.png")
// oauth
http.HandleFunc("/oauth/github/redirect", session.RedirectGitHubHandler)
http.HandleFunc("/oauth/github/callback", session.GithubCallbackHandler)
http.HandleFunc("/login/redirect", session.LoginRedirectHandler)
http.HandleFunc("/login/callback", session.LoginCallbackHandler)
// session
http.HandleFunc("/session/ws", handlerWrapper(session.WSHandler))
@ -106,7 +107,6 @@ func main() {
http.HandleFunc("/stop", handlerWrapper(output.StopHandler))
http.HandleFunc("/go/test", handlerWrapper(output.GoTestHandler))
http.HandleFunc("/go/vet", handlerWrapper(output.GoVetHandler))
http.HandleFunc("/go/mod", handlerWrapper(output.GoModHandler))
http.HandleFunc("/go/install", handlerWrapper(output.GoInstallHandler))
http.HandleFunc("/output/ws", handlerWrapper(output.WSHandler))
@ -132,7 +132,6 @@ func main() {
http.HandleFunc("/file/zip", handlerWrapper(file.GetZipHandler))
// editor
http.HandleFunc("/editor/ws", handlerWrapper(editor.WSHandler))
http.HandleFunc("/go/fmt", handlerWrapper(editor.GoFmtHandler))
http.HandleFunc("/autocomplete", handlerWrapper(editor.AutocompleteHandler))
http.HandleFunc("/exprinfo", handlerWrapper(editor.GetExprInfoHandler))
@ -159,7 +158,7 @@ func main() {
logger.Infof("Wide is running [%s]", conf.Wide.Server)
err := http.ListenAndServe("127.0.0.1:7070", nil)
err := http.ListenAndServe("0.0.0.0:7070", nil)
if err != nil {
logger.Error(err)
}
@ -204,7 +203,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"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", uid, len(wideSessions))
@ -255,7 +254,8 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
httpSession.Save(r, w)
uid := httpSession.Values["uid"].(string)
locale := conf.GetUser(uid).Locale
user := conf.GetUser(uid)
locale := user.Locale
userWorkspace := conf.GetUserWorkspace(uid)
sid := r.URL.Query()["sid"][0]
@ -265,7 +265,7 @@ func startHandler(w http.ResponseWriter, r *http.Request) {
}
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
"uid": uid, "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")
@ -409,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

@ -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 {

View File

@ -27,16 +27,22 @@ 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"
)
// 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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
@ -49,85 +55,40 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
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(uid, 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
}
suffix := ""
if util.OS.IsWindows() {
suffix = ".exe"
}
goBuildArgs := []string{}
goBuildArgs = append(goBuildArgs, "build", "-i")
goBuildArgs = append(goBuildArgs, user.BuildArgs(runtime.GOOS)...)
cmd := exec.Command("go", goBuildArgs...)
cmd.Dir = curDir
setCmdEnv(cmd, uid)
executable := filepath.Base(curDir) + suffix
executable = filepath.Join(curDir, executable)
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
}
fout.Close()
channelRet := map[string]interface{}{}
if nil != session.OutputWS[sid] {
// display "START [go build]" in front-end browser
@ -138,19 +99,69 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
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
err := wsChannel.WriteJSON(&channelRet)
if nil != err {
logger.Error(err)
return
}
wsChannel.Refresh()
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 gulu.OS.IsWindows() {
suffix = ".exe"
}
executable := filepath.Base(curDir) + suffix
executable = filepath.Join(curDir, executable)
stdout, err := cmd.StdoutPipe()
if nil != err {
logger.Error(err)
result.Code = -1
return
}
stderr, err := cmd.StderrPipe()
if nil != err {
logger.Error(err)
result.Code = -1
return
}
if 0 != result.Code {
return
}
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
return
}
@ -162,7 +173,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
/////////
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for {
wsChannel := session.OutputWS[sid]
@ -175,8 +186,14 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
break
}
_, ok := err.(*os.PathError)
if ok {
// 构建时报 “read |0: file already closed” https://github.com/b3log/wide/issues/363
break
}
if nil != err {
logger.Warn(err)
logger.Warnf("%#v", err)
break
}

View File

@ -26,16 +26,22 @@ 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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
@ -50,7 +56,7 @@ func CrossCompilationHandler(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
}
@ -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(uid, filePath) {
if gulu.Go.IsAPI(filePath) || !session.CanAccess(uid, filePath) {
http.Error(w, "Forbidden", http.StatusForbidden)
return
@ -106,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
}
@ -114,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
}
@ -146,13 +152,13 @@ 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()
// read all

View File

@ -26,16 +26,22 @@ 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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
@ -50,7 +56,7 @@ func GoInstallHandler(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
}
@ -70,7 +76,7 @@ func GoInstallHandler(w http.ResponseWriter, r *http.Request) {
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,13 +116,13 @@ 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]", uid, sid, runningId, curDir)

View File

@ -1,136 +0,0 @@
// 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 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"
)
// GoModHandler handles request of go mod.
func GoModHandler(w http.ResponseWriter, r *http.Request) {
result := util.NewResult()
defer util.RetResult(w, r, result)
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
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
return
}
sid := args["sid"].(string)
filePath := args["file"].(string)
curDir := filepath.Dir(filePath)
curDirName := filepath.Base(curDir)
cmd := exec.Command("go", "mod", "init", curDirName)
cmd.Dir = curDir
setCmdEnv(cmd, uid)
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 mod]" in front-end browser
channelRet["output"] = "<span class='start-mod'>" + i18n.Get(locale, "start-mod").(string) + "</span>\n"
channelRet["cmd"] = "start-mod"
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
}
runningId := rand.Int()
logger.Debugf("User [%s, %s] is running [go mod] [runningId=%d]", uid, sid, runningId)
channelRet = map[string]interface{}{}
channelRet["cmd"] = "go mod"
buf, _ := ioutil.ReadAll(reader)
output := string(buf)
err = cmd.Wait()
if nil != err || 0 != cmd.ProcessState.ExitCode() {
logger.Debugf("User [%s, %s] 's [go mod] [runningId=%d] has done (with error)", uid, sid, runningId)
channelRet["output"] = "<span class='mod-error'>" + i18n.Get(locale, "mod-error").(string) + "</span>\n" + output
} else {
logger.Debugf("User [%s, %s] 's running [go mod] [runningId=%d] has done", uid, sid, runningId)
channelRet["output"] = "<span class='mod-succ'>" + i18n.Get(locale, "mod-succ").(string) + "</span>\n" + output
}
wsChannel := session.OutputWS[sid]
if nil != wsChannel {
wsChannel.WriteJSON(&channelRet)
wsChannel.Refresh()
}
}

View File

@ -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 {
@ -122,7 +122,7 @@ func setCmdEnv(cmd *exec.Cmd, uid string) {
"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 {

View File

@ -15,7 +15,7 @@
package output
import (
"github.com/b3log/wide/session"
"github.com/88250/wide/session"
"net/http"
)

View File

@ -24,16 +24,16 @@ 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, session.CookieName)
if httpSession.IsNew {
@ -48,7 +48,7 @@ func GoTestHandler(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
}
@ -66,7 +66,7 @@ func GoTestHandler(w http.ResponseWriter, r *http.Request) {
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,13 +106,13 @@ 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]", uid, sid, runningId)

View File

@ -24,16 +24,16 @@ 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, session.CookieName)
if httpSession.IsNew {
@ -48,7 +48,7 @@ func GoVetHandler(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
}
@ -66,7 +66,7 @@ func GoVetHandler(w http.ResponseWriter, r *http.Request) {
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,13 +106,13 @@ 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]", uid, sid, runningId)

506
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wide",
"version": "1.4.0",
"version": "1.6.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -36,15 +36,9 @@
}
},
"acorn": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
"dev": true
},
"amdefine": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
"integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
"dev": true
},
"ansi-colors": {
@ -71,12 +65,6 @@
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
"ansi-wrap": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
@ -144,12 +132,6 @@
"integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
"dev": true
},
"array-differ": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz",
"integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=",
"dev": true
},
"array-each": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
@ -216,12 +198,6 @@
}
}
},
"array-uniq": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
"dev": true
},
"array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@ -345,12 +321,6 @@
}
}
},
"beeper": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz",
"integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=",
"dev": true
},
"binary-extensions": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
@ -408,41 +378,6 @@
"integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
"dev": true
},
"bufferstreams": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bufferstreams/-/bufferstreams-1.0.1.tgz",
"integrity": "sha1-z7GtlWjTujz+k1upq92VLeiKqyo=",
"dev": true,
"requires": {
"readable-stream": "^1.0.33"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
}
},
"cache-base": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
@ -466,19 +401,6 @@
"integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
"dev": true
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"chokidar": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
@ -530,27 +452,6 @@
}
}
},
"clean-css": {
"version": "3.4.28",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-3.4.28.tgz",
"integrity": "sha1-vxlF6C/ICPVWlebd6uwBQA79A/8=",
"dev": true,
"requires": {
"commander": "2.8.x",
"source-map": "0.4.x"
},
"dependencies": {
"source-map": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
"integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
"dev": true,
"requires": {
"amdefine": ">=0.0.4"
}
}
}
},
"cliui": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
@ -632,15 +533,6 @@
"integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
"dev": true
},
"commander": {
"version": "2.8.1",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.8.1.tgz",
"integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=",
"dev": true,
"requires": {
"graceful-readlink": ">= 1.0.0"
}
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
@ -742,12 +634,6 @@
"es5-ext": "^0.10.9"
}
},
"dateformat": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz",
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=",
"dev": true
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@ -882,41 +768,6 @@
"integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=",
"dev": true
},
"duplexer2": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz",
"integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=",
"dev": true,
"requires": {
"readable-stream": "~1.1.9"
},
"dependencies": {
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
"dev": true
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.1",
"isarray": "0.0.1",
"string_decoder": "~0.10.x"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
}
},
"duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@ -1001,12 +852,6 @@
"es6-symbol": "^3.1.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
@ -1950,12 +1795,6 @@
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=",
"dev": true
},
"gulp": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
@ -1996,6 +1835,44 @@
}
}
},
"gulp-clean-css": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-4.2.0.tgz",
"integrity": "sha512-r4zQsSOAK2UYUL/ipkAVCTRg/2CLZ2A+oPVORopBximRksJ6qy3EX1KGrIWT4ZrHxz3Hlobb1yyJtqiut7DNjA==",
"dev": true,
"requires": {
"clean-css": "4.2.1",
"plugin-error": "1.0.1",
"through2": "3.0.1",
"vinyl-sourcemaps-apply": "0.2.1"
},
"dependencies": {
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
"integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==",
"dev": true,
"requires": {
"source-map": "~0.6.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"through2": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
"integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
"dev": true,
"requires": {
"readable-stream": "2 || 3"
}
}
}
},
"gulp-concat": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz",
@ -2007,20 +1884,6 @@
"vinyl": "^2.0.0"
}
},
"gulp-minify-css": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/gulp-minify-css/-/gulp-minify-css-1.2.4.tgz",
"integrity": "sha1-thZJV2Auon+eWtiCJ2ld0gV3jAY=",
"dev": true,
"requires": {
"clean-css": "^3.3.3",
"gulp-util": "^3.0.5",
"object-assign": "^4.0.1",
"readable-stream": "^2.0.0",
"vinyl-bufferstream": "^1.0.1",
"vinyl-sourcemaps-apply": "^0.2.0"
}
},
"gulp-sourcemaps": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz",
@ -2066,69 +1929,6 @@
"vinyl-sourcemaps-apply": "^0.2.0"
}
},
"gulp-util": {
"version": "3.0.8",
"resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz",
"integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=",
"dev": true,
"requires": {
"array-differ": "^1.0.0",
"array-uniq": "^1.0.2",
"beeper": "^1.0.0",
"chalk": "^1.0.0",
"dateformat": "^2.0.0",
"fancy-log": "^1.1.0",
"gulplog": "^1.0.0",
"has-gulplog": "^0.1.0",
"lodash._reescape": "^3.0.0",
"lodash._reevaluate": "^3.0.0",
"lodash._reinterpolate": "^3.0.0",
"lodash.template": "^3.0.0",
"minimist": "^1.1.0",
"multipipe": "^0.1.2",
"object-assign": "^3.0.0",
"replace-ext": "0.0.1",
"through2": "^2.0.0",
"vinyl": "^0.5.0"
},
"dependencies": {
"clone": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
"dev": true
},
"clone-stats": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz",
"integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=",
"dev": true
},
"object-assign": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=",
"dev": true
},
"replace-ext": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz",
"integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=",
"dev": true
},
"vinyl": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz",
"integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=",
"dev": true,
"requires": {
"clone": "^1.0.0",
"clone-stats": "^0.0.1",
"replace-ext": "0.0.1"
}
}
}
},
"gulplog": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
@ -2138,15 +1938,6 @@
"glogg": "^1.0.0"
}
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
},
"has-gulplog": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz",
@ -2542,125 +2333,6 @@
"strip-bom": "^2.0.0"
}
},
"lodash._basecopy": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz",
"integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
"dev": true
},
"lodash._basetostring": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz",
"integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=",
"dev": true
},
"lodash._basevalues": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz",
"integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=",
"dev": true
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
"integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=",
"dev": true
},
"lodash._isiterateecall": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz",
"integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=",
"dev": true
},
"lodash._reescape": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz",
"integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=",
"dev": true
},
"lodash._reevaluate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz",
"integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=",
"dev": true
},
"lodash._reinterpolate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
"dev": true
},
"lodash._root": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz",
"integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=",
"dev": true
},
"lodash.escape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz",
"integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=",
"dev": true,
"requires": {
"lodash._root": "^3.0.0"
}
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
"integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=",
"dev": true
},
"lodash.isarray": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
"integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=",
"dev": true,
"requires": {
"lodash._getnative": "^3.0.0",
"lodash.isarguments": "^3.0.0",
"lodash.isarray": "^3.0.0"
}
},
"lodash.restparam": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
"integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=",
"dev": true
},
"lodash.template": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz",
"integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=",
"dev": true,
"requires": {
"lodash._basecopy": "^3.0.0",
"lodash._basetostring": "^3.0.0",
"lodash._basevalues": "^3.0.0",
"lodash._isiterateecall": "^3.0.0",
"lodash._reinterpolate": "^3.0.0",
"lodash.escape": "^3.0.0",
"lodash.keys": "^3.0.0",
"lodash.restparam": "^3.0.0",
"lodash.templatesettings": "^3.0.0"
}
},
"lodash.templatesettings": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz",
"integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=",
"dev": true,
"requires": {
"lodash._reinterpolate": "^3.0.0",
"lodash.escape": "^3.0.0"
}
},
"lru-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
@ -2790,16 +2462,10 @@
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"dev": true,
"requires": {
"for-in": "^1.0.2",
@ -2823,15 +2489,6 @@
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"multipipe": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz",
"integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=",
"dev": true,
"requires": {
"duplexer2": "0.0.2"
}
},
"mute-stdout": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
@ -3144,6 +2801,18 @@
"pinkie": "^2.0.0"
}
},
"plugin-error": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
"dev": true,
"requires": {
"ansi-colors": "^1.0.1",
"arr-diff": "^4.0.0",
"arr-union": "^3.1.0",
"extend-shallow": "^3.0.2"
}
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@ -3402,9 +3071,9 @@
"dev": true
},
"set-value": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz",
"integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==",
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
"integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
@ -3686,12 +3355,6 @@
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=",
"dev": true
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
},
"sver-compat": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz",
@ -3859,38 +3522,15 @@
"dev": true
},
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
"integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
"integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
"dev": true,
"requires": {
"arr-union": "^3.1.0",
"get-value": "^2.0.6",
"is-extendable": "^0.1.1",
"set-value": "^0.4.3"
},
"dependencies": {
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"dev": true,
"requires": {
"is-extendable": "^0.1.0"
}
},
"set-value": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz",
"integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=",
"dev": true,
"requires": {
"extend-shallow": "^2.0.1",
"is-extendable": "^0.1.1",
"is-plain-object": "^2.0.1",
"to-object-path": "^0.3.0"
}
}
"set-value": "^2.0.1"
}
},
"unique-stream": {
@ -4006,15 +3646,6 @@
"replace-ext": "^1.0.0"
}
},
"vinyl-bufferstream": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/vinyl-bufferstream/-/vinyl-bufferstream-1.0.1.tgz",
"integrity": "sha1-BTeGn1gO/6TKRay0dXnkuf5jCBo=",
"dev": true,
"requires": {
"bufferstreams": "1.0.1"
}
},
"vinyl-fs": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
@ -4129,12 +3760,13 @@
}
},
"yargs-parser": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
"integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz",
"integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==",
"dev": true,
"requires": {
"camelcase": "^3.0.0"
"camelcase": "^3.0.0",
"object.assign": "^4.1.0"
}
}
}

View File

@ -1,14 +1,14 @@
{
"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,
@ -28,8 +28,8 @@
},
"devDependencies": {
"gulp": "^4.0.2",
"gulp-clean-css": "^4.2.0",
"gulp-concat": "^2.6.1",
"gulp-minify-css": "^1.2.4",
"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

@ -18,18 +18,24 @@ 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(), http.StatusInternalServerError)
@ -48,16 +54,24 @@ 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)

View File

@ -22,15 +22,21 @@ 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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
httpSession, _ := session.HTTPSession.Get(r, session.CookieName)
if httpSession.IsNew {
@ -42,7 +48,7 @@ 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
}
@ -51,7 +57,7 @@ func BuildHandler(w http.ResponseWriter, r *http.Request) {
filePath := filepath.Clean(conf.Wide.Data + "/playground/" + fileName)
suffix := ""
if util.OS.IsWindows() {
if gulu.OS.IsWindows() {
suffix = ".exe"
}
@ -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

@ -24,15 +24,21 @@ import (
"os/exec"
"path/filepath"
"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)
if conf.Wide.ReadOnly {
result.Code = -1
result.Msg = "readonly mode"
return
}
session, _ := session.HTTPSession.Get(r, session.CookieName)
if session.IsNew {
@ -44,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
}
@ -57,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
}
@ -89,7 +95,7 @@ func SaveHandler(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

@ -26,16 +26,16 @@ 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) {
@ -55,11 +55,11 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
// 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.Data+ "/playground/" + fileNameArg)
filePath := filepath.Clean(conf.Wide.Data + "/playground/" + fileNameArg)
bytes, err := ioutil.ReadFile(filePath)
if nil != err {

View File

@ -15,7 +15,7 @@
package playground
import (
"github.com/b3log/wide/session"
"github.com/88250/wide/session"
"net/http"
)

View File

@ -15,7 +15,7 @@
package session
import (
"crypto/tls"
"fmt"
"html/template"
"math/rand"
"net/http"
@ -25,45 +25,26 @@ import (
"strings"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/i18n"
"github.com/b3log/wide/util"
"github.com/parnurzeal/gorequest"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/i18n"
"github.com/88250/wide/util"
)
var states = map[string]string{}
// RedirectGitHubHandler redirects to GitHub auth page.
func RedirectGitHubHandler(w http.ResponseWriter, r *http.Request) {
requestResult := util.NewResult()
_, _, errs := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
Get("https://hacpai.com/oauth/wide/client").
Set("user-agent", conf.UserAgent).Timeout(10 * time.Second).EndStruct(requestResult)
if nil != errs {
logger.Errorf("Get oauth client id failed: %+v", errs)
http.Error(w, "Get oauth info failed", http.StatusInternalServerError)
// 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"
return
}
if 0 != requestResult.Code {
logger.Errorf("get oauth client id failed [code=%d, msg=%s]", requestResult.Code, requestResult.Msg)
http.Error(w, "Get oauth info failed", http.StatusNotFound)
return
}
data := requestResult.Data.(map[string]interface{})
clientId := data["clientId"].(string)
loginAuthURL := data["loginAuthURL"].(string)
state := r.URL.Query().Get("state")
referer := conf.Wide.Server + "__" + state
state = util.Rand.String(16) + referer
// надо будет добавить ttlcache для state и проверять для предотвращения атак CSRF
state := gulu.Rand.String(16)
states[state] = state
path := loginAuthURL + "?client_id=" + clientId + "&state=" + state + "&scope=public_repo,read:user,user:follow"
path := loginAuthURL + "&state=" + state + "&client_id=" + conf.Wide.OAuthClientID
http.Redirect(w, r, path, http.StatusSeeOther)
}
func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
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)
@ -72,31 +53,37 @@ func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
}
delete(states, state)
referer := state[16:]
if strings.Contains(referer, "__0") || strings.Contains(referer, "__1") {
referer = referer[:len(referer)-len("__0")]
}
accessToken := r.URL.Query().Get("ak")
githubUser := GitHubUserInfo(accessToken)
if nil == githubUser {
logger.Warnf("Can not get user info with token [" + accessToken + "]")
http.Error(w, "Get user info failed", http.StatusUnauthorized)
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
}
githubId := githubUser["userId"].(string)
userName := githubUser["userName"].(string)
avatar := githubUser["userAvatarURL"].(string)
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)
user := conf.GetUser(githubId)
return
}
userId := userInfo["userId"].(string)
userName := userInfo["userName"].(string)
avatar := userInfo["avatar"].(string)
user := conf.GetUser(userId)
if nil == user {
msg := addUser(githubId, userName, avatar)
msg := addUser(userId, userName, avatar)
if userCreated != msg {
result := util.NewResult()
result.Succ = false
result := gulu.Ret.NewResult()
result.Code = -1
result.Msg = msg
util.RetResult(w, r, result)
gulu.Ret.RetResult(w, r, result)
return
}
@ -104,7 +91,7 @@ func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
// create a HTTP session
httpSession, _ := HTTPSession.Get(r, CookieName)
httpSession.Values["uid"] = githubId
httpSession.Values["uid"] = userId
httpSession.Values["id"] = strconv.Itoa(rand.Int())
httpSession.Options.MaxAge = conf.Wide.HTTPSessionMaxAge
httpSession.Save(r, w)
@ -112,25 +99,6 @@ func GithubCallbackHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther)
}
// GitHubUserInfo returns GitHub user info specified by the given access token.
func GitHubUserInfo(accessToken string) (ret map[string]interface{}) {
result := map[string]interface{}{}
response, data, errors := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
Get("https://hacpai.com/github/user?ak="+accessToken).Timeout(7*time.Second).
Set("User-Agent", conf.UserAgent).EndStruct(&result)
if nil != errors || http.StatusOK != response.StatusCode {
logger.Errorf("Get github user info failed: %+v, %s", errors, data)
return nil
}
if 0 != result["sc"].(float64) {
return nil
}
return result["data"].(map[string]interface{})
}
// 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),
@ -149,8 +117,8 @@ func LoginHandler(w http.ResponseWriter, r *http.Request) {
// LogoutHandler handles request of user logout (exit).
func LogoutHandler(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, _ := HTTPSession.Get(r, CookieName)

View File

@ -15,10 +15,8 @@
package session
import (
"bufio"
"bytes"
"encoding/json"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/util"
"math/rand"
"net/http"
"os"
@ -28,6 +26,10 @@ import (
"strings"
"sync"
"time"
"github.com/88250/gulu"
"github.com/88250/wide/conf"
"github.com/88250/wide/util"
)
// Type of process set.
@ -43,20 +45,26 @@ 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 := 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
}
sid := args["sid"].(string)
wSession := WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
result.Code = -1
}
filePath := args["executable"].(string)
@ -66,35 +74,27 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
var cmd *exec.Cmd
if conf.Docker {
fileName := filepath.Base(filePath)
cmd = exec.Command("docker", "run", "--rm", "--cpus", "0.05", "--name", rid, "-v", filePath+":/"+fileName, conf.DockerImageGo, "/"+fileName)
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
}
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)
outBuf := &bytes.Buffer{}
errBuf := &bytes.Buffer{}
cmd.Stdout = outBuf
cmd.Stderr = errBuf
if err := cmd.Start(); nil != err {
logger.Error(err)
result.Succ = false
result.Code = -1
}
wsChannel := channel[sid]
channelRet := map[string]interface{}{}
if !result.Succ {
if 0 != result.Code {
channelRet["cmd"] = "run-done"
channelRet["output"] = ""
wsChannel.WriteJSON(&channelRet)
@ -108,6 +108,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
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 {
@ -120,19 +121,27 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
}
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
logger.Debugf("User [%s, %s] is running [id=%s, file=%s]", wSession.UserId, sid, rid, filePath)
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for {
r, _, err := outReader.ReadRune()
if nil != err {
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)
@ -147,11 +156,21 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
}()
for {
r, _, err := errReader.ReadRune()
if nil != err {
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)
@ -184,6 +203,7 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
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)
@ -196,13 +216,13 @@ func RunHandler(w http.ResponseWriter, r *http.Request, channel map[string]*util
// 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)
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
}
@ -212,7 +232,7 @@ func StopHandler(w http.ResponseWriter, r *http.Request) {
wSession := WideSessions.Get(sid)
if nil == wSession {
result.Succ = false
result.Code = -1
return
}

View File

@ -35,10 +35,10 @@ import (
"sync"
"time"
"github.com/b3log/wide/conf"
"github.com/b3log/wide/event"
"github.com/b3log/wide/log"
"github.com/b3log/wide/util"
"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"
@ -52,7 +52,7 @@ const (
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var logger = gulu.Log.NewLogger(os.Stdout)
var (
// SessionWS holds all session channels. <sid, *util.WSChannel>
@ -105,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")
@ -139,9 +139,9 @@ func (u *userReport) report() string {
// 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) {
for _ = range time.Tick(10 * time.Minute) {
users := userReports{}
processSum := 0
@ -284,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
@ -294,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
}
@ -477,7 +477,7 @@ func (sessions *wSessions) new(httpSession *sessions.Session, sid string) *WideS
}
go func() {
defer util.Recover()
defer gulu.Panic.Recover(nil)
for {
ch := SessionWS[sid]
@ -492,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)
@ -500,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 {
@ -526,7 +526,7 @@ 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(uid))
for _, workspace := range workspaces {

View File

@ -24,9 +24,9 @@ import (
"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 (
@ -67,7 +67,7 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
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")
@ -93,8 +93,8 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
// 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
@ -117,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
}
@ -146,7 +146,11 @@ func PreferenceHandler(w http.ResponseWriter, r *http.Request) {
user.Lived = now
user.Updated = now
result.Succ = user.Save()
if user.Save() {
result.Code = 0
} else {
result.Code = -1
}
}
// FixedTimeSave saves online users' configurations periodically (1 minute).
@ -154,7 +158,7 @@ func PreferenceHandler(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) {
SaveOnlineUsers()

File diff suppressed because one or more lines are too long

View File

@ -14,8 +14,16 @@
* limitations under the License.
*/
body{
overflow: auto;
body {
display: flex;
flex-direction: column;
max-height: 100vh;
}
.main {
flex: 1;
min-height: 1px;
display: flex;
}
.header {
@ -61,7 +69,7 @@ body{
}
.share-panel .font-ico:hover {
transform:rotate(360deg);
transform: rotate(360deg);
}
.footer {

View File

@ -92,18 +92,16 @@ body {
}
.login__github {
cursor: pointer;
background-image: url("/static/images/github.png");
height: 200px;
.login__icon {
width: 200px;
background-size: cover;
margin: 20px auto;
background-position: center center;
transition: all .15s ease-in-out;
padding-right: 24px;
color: #3b3e43;
fill: currentColor;
}
.login__github:hover {
background-image: url("/static/images/github.gif");
.login__icon:hover {
transform: scale(1.1);
}
.btn {
@ -127,6 +125,14 @@ body {
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;
@ -216,4 +222,20 @@ body {
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

@ -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-mod {
.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 .mod-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 .mod-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?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 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: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{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-install,.bottom-window-group .output .start-mod,.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 .mod-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 .mod-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}#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}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

BIN
static/images/hacpai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -248,7 +248,7 @@ var editors = {
after: function () {
$("#startPage").load('/start?sid=' + config.wideSessionId);
$.ajax({
url: "https://hacpai.com/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",
@ -265,7 +265,7 @@ var editors = {
}
var listHTML = "<ul><li class='title'>" + config.label.community +
"<a href='https://hacpai.com/article/1437497122181' target='_blank' class='fn-right'>边看边练</li>";
"<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>"
@ -439,7 +439,7 @@ var editors = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -587,7 +587,7 @@ var editors = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -617,7 +617,7 @@ var editors = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}

60
static/js/lib.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -163,7 +163,7 @@ var menu = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
window.location.href = "/login";
}
}
@ -172,33 +172,6 @@ var menu = {
openAbout: function () {
$("#dialogAbout").dialog("open");
},
gomod: function () {
menu.saveAllFiles();
var currentPath = editors.getCurrentPath();
if (!currentPath) {
return false;
}
if ($(".menu li.go-mod").hasClass("disabled")) {
return false;
}
var request = newWideRequest();
request.file = currentPath;
$.ajax({
type: 'POST',
url: '/go/mod',
data: JSON.stringify(request),
dataType: "json",
beforeSend: function () {
bottomGroup.resetOutput();
},
success: function (result) {
}
});
},
goinstall: function () {
menu.saveAllFiles();
@ -467,7 +440,7 @@ var menu = {
url: '/preference',
data: JSON.stringify(request),
success: function (result, textStatus, jqXHR) {
if (!result.succ) {
if (0 != result.code) {
return false;
}

View File

@ -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

@ -19,14 +19,13 @@
*
* @author <a href="http://vanessa.b3log.org">Liyuan Li</a>
* @author <a href="http://88250.b3log.org">Liang Ding</a>
* @version 1.0.0.2, Oct 5, 2018
* @version 1.0.0.3, Jun 23, 2019
*/
var playground = {
autocompleteMutex: false,
editor: undefined,
pid: undefined,
_resize: function () {
$('#goNews, #editorDivWrap').height($(window).height() - 40 - $(".footer").height());
playground.editor.setSize("auto", ($("#editorDiv").parent().height() * 0.7) + "px");
},
_initShare: function () {
@ -82,7 +81,6 @@ var playground = {
var autocompleteHints = [];
if (playground.autocompleteMutex && editor.state.completionActive) {
console.log(1);
return;
}
@ -275,23 +273,23 @@ var playground = {
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) {
@ -303,22 +301,30 @@ var playground = {
playground.pid = data.pid;
var output = 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/>');
var oldOutput = $("#output").html();
$("#output").html(oldOutput + output);
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://hacpai.com/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",
@ -331,7 +337,7 @@ var playground = {
var length = articles.length;
var listHTML = "<ul><li class='title'>" + config.label.community +
"<a href='https://hacpai.com/article/1437497122181' target='_blank' class='fn-right'>边看边练</li>";
"<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>"
@ -365,8 +371,7 @@ var playground = {
playground.editor.setValue(data.code);
if (!result.succ) {
console.log(data);
if (0 != result.code) {
return;
}
@ -438,7 +443,7 @@ var playground = {
playground.editor.setValue(data.code);
playground.editor.setCursor(cursor);
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -452,13 +457,11 @@ var playground = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
console.log(result);
var data = result.data;
$("#output").html(data.output);
if (!result.succ) {
if (0 != result.code) {
return;
}

View File

@ -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 () {
@ -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

@ -164,7 +164,7 @@ var tree = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -190,7 +190,7 @@ var tree = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -220,7 +220,7 @@ var tree = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
var $dirRMenu = $("#dirRMenu");
var $fileRMenu = $("#fileRMenu");
var setting = {
@ -374,7 +374,7 @@ var tree = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -463,7 +463,7 @@ var tree = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -499,7 +499,7 @@ var tree = {
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();

View File

@ -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 wide = {
curNode: undefined,
@ -42,7 +42,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -125,7 +125,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogRemoveConfirm").dialog("close");
bottomGroup.tabs.setCurrent("notification");
windows.flowBottom();
@ -163,7 +163,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogNewFilePrompt").dialog("close");
bottomGroup.tabs.setCurrent("notification");
windows.flowBottom();
@ -207,7 +207,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogNewDirPrompt").dialog("close");
bottomGroup.tabs.setCurrent("notification");
windows.flowBottom();
@ -268,7 +268,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
return;
}
@ -343,11 +343,11 @@ var wide = {
_initWS: function () {
var outputWS = new ReconnectingWebSocket(config.channel + '/output/ws?sid=' + config.wideSessionId);
outputWS.onopen = function () {
console.log('[output onopen] connected');
// console.log('[output onopen] connected');
};
outputWS.onmessage = function (e) {
console.log('[output onmessage]' + e.data);
// console.log('[output onmessage]' + e.data);
var data = JSON.parse(e.data);
if (goLintFound) {
@ -390,14 +390,12 @@ var wide = {
case 'start-test':
case 'start-vet':
case 'start-install':
case 'start-mod':
bottomGroup.fillOutput(data.output);
break;
case 'go test':
case 'go vet':
case 'go install':
case 'go mod':
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + data.output);
break;
@ -445,7 +443,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (!result.succ) {
if (0 != result.code) {
$("#dialogAlert").dialog("open", result.msg);
return false;
@ -465,10 +463,10 @@ var wide = {
}
};
outputWS.onclose = function (e) {
console.log('[output onclose] disconnected (' + e.code + ')');
// console.log('[output onclose] disconnected (' + e.code + ')');
};
outputWS.onerror = function (e) {
console.log('[output onerror]');
console.log('[output onerror]',e);
};
},
_initFooter: function () {
@ -616,7 +614,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
editor.setValue(result.data.code);
editor.setCursor(cursor);
editor.scrollTo(null, scrollInfo.top);
@ -649,7 +647,7 @@ var wide = {
data: JSON.stringify(request),
dataType: "json",
success: function (result) {
if (result.succ) {
if (0 == result.code) {
formatted = result.data.code;
}
}

16
static/js/wide.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
This directory is used to unit-test.

View File

@ -1,159 +0,0 @@
// 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 util
import (
"io"
"os"
"path/filepath"
"strings"
"github.com/b3log/wide/log"
)
// Logger.
var fileLogger = log.NewLogger(os.Stdout)
type myfile struct{}
// File utilities.
var File = myfile{}
// GetFileSize get the length in bytes of file of the specified path.
func (*myfile) GetFileSize(path string) int64 {
fi, err := os.Stat(path)
if nil != err {
fileLogger.Error(err)
return -1
}
return fi.Size()
}
// IsExist determines whether the file spcified by the given path is exists.
func (*myfile) IsExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
// IsBinary determines whether the specified content is a binary file content.
func (*myfile) IsBinary(content string) bool {
for _, b := range content {
if 0 == b {
return true
}
}
return false
}
// IsImg determines whether the specified extension is a image.
func (*myfile) IsImg(extension string) bool {
ext := strings.ToLower(extension)
switch ext {
case ".jpg", ".jpeg", ".bmp", ".gif", ".png", ".svg", ".ico":
return true
default:
return false
}
}
// IsDir determines whether the specified path is a directory.
func (*myfile) IsDir(path string) bool {
fio, err := os.Lstat(path)
if nil != err {
fileLogger.Warnf("Determines whether [%s] is a directory failed: [%v]", path, err)
return false
}
return fio.IsDir()
}
// CopyFile copies the source file to the dest file.
func (*myfile) CopyFile(source string, dest string) (err error) {
sourcefile, err := os.Open(source)
if err != nil {
return err
}
defer sourcefile.Close()
destfile, err := os.Create(dest)
if err != nil {
return err
}
defer destfile.Close()
_, err = io.Copy(destfile, sourcefile)
if err == nil {
sourceinfo, err := os.Stat(source)
if err != nil {
err = os.Chmod(dest, sourceinfo.Mode())
}
}
return nil
}
// CopyDir copies the source directory to the dest directory.
func (*myfile) CopyDir(source string, dest string) (err error) {
sourceinfo, err := os.Stat(source)
if err != nil {
return err
}
// create dest dir
err = os.MkdirAll(dest, sourceinfo.Mode())
if err != nil {
return err
}
directory, err := os.Open(source)
if err != nil {
return err
}
defer directory.Close()
objects, err := directory.Readdir(-1)
if err != nil {
return err
}
for _, obj := range objects {
srcFilePath := filepath.Join(source, obj.Name())
destFilePath := filepath.Join(dest, obj.Name())
if obj.IsDir() {
// create sub-directories - recursively
err = File.CopyDir(srcFilePath, destFilePath)
if err != nil {
fileLogger.Error(err)
}
} else {
err = File.CopyFile(srcFilePath, destFilePath)
if err != nil {
fileLogger.Error(err)
}
}
}
return nil
}

View File

@ -1,81 +0,0 @@
// 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 util
import (
"path/filepath"
"strconv"
"testing"
)
func TestGetFileSize(t *testing.T) {
size := File.GetFileSize(".")
t.Log("size of file [.] is [" + strconv.FormatInt(size, 10) + "]")
}
func TestIsExist(t *testing.T) {
if !File.IsExist(".") {
t.Error(". must exist")
return
}
}
func TestIdBinary(t *testing.T) {
if File.IsBinary("not binary content") {
t.Error("The content should not be binary")
return
}
}
func TestIsImg(t *testing.T) {
if !File.IsImg(".jpg") {
t.Error(".jpg should be a valid extension of a image file")
return
}
}
func TestIsDir(t *testing.T) {
if !File.IsDir(".") {
t.Error(". should be a directory")
return
}
}
func TestCopyDir(t *testing.T) {
dest := filepath.Join(testDir, "util")
err := File.CopyDir(".", dest)
if nil != err {
t.Error("Copy dir error: ", err)
return
}
}
func TestCopyFile(t *testing.T) {
dest := filepath.Join(testDir, "file.go")
err := File.CopyFile("./file.go", dest)
if nil != err {
t.Error("Copy file error: ", err)
return
}
}

View File

@ -1,118 +0,0 @@
// 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 util
import (
"os"
"path"
"path/filepath"
"runtime"
"sort"
"strings"
)
const (
pathSeparator = string(os.PathSeparator) // OS-specific path separator
pathListSeparator = string(os.PathListSeparator) // OS-specific path list separator
)
type mygo struct{}
// Go utilities.
var Go = mygo{}
func (*mygo) GetCrossPlatforms() []string {
return []string{
"darwin_amd64", "linux_amd64", "windows_amd64",
"linux_arm", "darwin_386", "linux_386", "windows_386"}
}
// GetAPIPath gets the Go source code path.
//
// 1. before Go 1.4: $GOROOT/src/pkg
// 2. Go 1.4 and after: $GOROOT/src
func (*mygo) GetAPIPath() string {
ret := runtime.GOROOT() + "/src/pkg" // before Go 1.4
if !File.IsExist(ret) {
ret = runtime.GOROOT() + "/src" // Go 1.4 and after
}
return filepath.FromSlash(path.Clean(ret))
}
// IsAPI determines whether the specified path belongs to Go API.
func (*mygo) IsAPI(path string) bool {
apiPath := Go.GetAPIPath()
return strings.HasPrefix(filepath.FromSlash(path), apiPath)
}
// GetGoFormats gets Go format tools. It may return ["gofmt", "goimports"].
func (*mygo) GetGoFormats() []string {
ret := []string{"gofmt"}
p := Go.GetExecutableInGOBIN("goimports")
if File.IsExist(p) {
ret = append(ret, "goimports")
}
sort.Strings(ret)
return ret
}
// GetExecutableInGOBIN gets executable file under GOBIN path.
//
// The specified executable should not with extension, this function will append .exe if on Windows.
func (*mygo) GetExecutableInGOBIN(executable string) string {
if OS.IsWindows() {
executable += ".exe"
}
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
for _, gopath := range gopaths {
// $GOPATH/bin/$GOOS_$GOARCH/executable
ret := gopath + pathSeparator + "bin" + pathSeparator +
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + pathSeparator + executable
if File.IsExist(ret) {
return ret
}
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
ret = gopath + pathSeparator + "bin" + pathSeparator +
runtime.GOOS + "_" + runtime.GOARCH + pathSeparator + executable
if File.IsExist(ret) {
return ret
}
// $GOPATH/bin/executable
ret = gopath + pathSeparator + "bin" + pathSeparator + executable
if File.IsExist(ret) {
return ret
}
}
// $GOBIN/executable
gobin := os.Getenv("GOBIN")
if "" != gobin {
ret := gobin + pathSeparator + executable
if File.IsExist(ret) {
return ret
}
}
return "./" + executable
}

View File

@ -1,101 +0,0 @@
// 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 util
import (
"runtime"
"strings"
"testing"
"github.com/hashicorp/go-version"
)
func TestGetCrossPlatforms(t *testing.T) {
crossPlatforms := Go.GetCrossPlatforms()
if len(crossPlatforms) < 1 {
t.Error("should have one platform at least")
}
}
func TestGetAPIPath(t *testing.T) {
apiPath := Go.GetAPIPath()
v := runtime.Version()[2:]
ver, err := version.NewVersion(v)
if nil != err {
t.Error(err)
return
}
constraints, err := version.NewConstraint(">= 1.4")
if nil != err {
t.Error(err)
return
}
if constraints.Check(ver) {
if !strings.HasSuffix(apiPath, "src") {
t.Error("api path should end with \"src\"")
return
}
} else {
if !strings.HasSuffix(apiPath, "pkg") {
t.Error("api path should end with \"pkg\"")
}
}
}
func TestIsAPI(t *testing.T) {
apiPath := Go.GetAPIPath()
if !Go.IsAPI(apiPath) {
t.Error("api path root should belong to api path")
return
}
root := "/root"
if Go.IsAPI(root) {
t.Error("root should not belong to api path")
return
}
}
func TestGetGoFormats(t *testing.T) {
formats := Go.GetGoFormats()
if len(formats) < 1 {
t.Error("should have one go format tool [gofmt] at least")
}
}
func TestGetExecutableInGOBIN(t *testing.T) {
bin := Go.GetExecutableInGOBIN("test")
if OS.IsWindows() {
if !strings.HasSuffix(bin, ".exe") {
t.Error("Executable binary should end with .exe")
return
}
}
}

47
util/hacpais.go Normal file
View File

@ -0,0 +1,47 @@
// 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 util
import (
"crypto/tls"
"net/http"
"os"
"time"
"github.com/88250/gulu"
"github.com/parnurzeal/gorequest"
)
// Logger
var logger = gulu.Log.NewLogger(os.Stdout)
// HacPaiURL is the URL of HacPai community.
const HacPaiURL = "https://ld246.com"
// HacPaiUserInfo returns HacPai community user info specified by the given access token.
func HacPaiUserInfo(accessToken string) (ret map[string]interface{}) {
result := map[string]interface{}{}
response, data, errors := gorequest.New().TLSClientConfig(&tls.Config{InsecureSkipVerify: true}).
Post(HacPaiURL+"/user/ak").SendString("access_token="+accessToken).Timeout(7*time.Second).
Set("User-Agent", "Pipe; +https://github.com/88250/wide").EndStruct(&result)
if nil != errors || http.StatusOK != response.StatusCode {
logger.Errorf("get community user info failed: %+v, %s", errors, data)
return nil
}
if 0 != result["code"].(float64) {
return nil
}
return result["data"].(map[string]interface{})
}

View File

@ -1,45 +0,0 @@
// 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 util includes common utilities.
package util
import (
"errors"
"net"
)
type mynet struct{}
// Network utilities.
var Net = mynet{}
// LocalIP gets the first NIC's IP address.
func (*mynet) LocalIP() (string, error) {
addrs, err := net.InterfaceAddrs()
if nil != err {
return "", err
}
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if nil != ipnet.IP.To4() {
return ipnet.IP.String(), nil
}
}
}
return "", errors.New("can't get local IP")
}

View File

@ -1,27 +0,0 @@
// 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 util
import "testing"
func TestLocalIP(t *testing.T) {
ip, err := Net.LocalIP()
if nil != err {
t.Error(err)
}
t.Log(ip)
}

65
util/oauth_token.go Normal file
View File

@ -0,0 +1,65 @@
package util
import (
"fmt"
"github.com/davidebianchi/go-jsonclient"
"net/http"
"net/url"
)
type oAuthAccessTokenReq struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
Code string `json:"code"`
GrantType string `json:"grant_type"`
RedirectURI string `json:"redirect_uri"`
}
type oAuthAccessTokenResp struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
RefreshToken string `json:"refresh_token"`
}
func GetOAuthToken(oAuthAccessTokenURL, clientID, clientSecret, code, redirectURI string) (string, error) {
u, err := url.Parse(oAuthAccessTokenURL)
if err != nil {
logger.Errorf(`failed to parse oAuthAccessTokenURL. error: %s`, err.Error())
return ``, err
}
client, err := jsonclient.New(jsonclient.Options{
BaseURL: fmt.Sprintf(`%s://%s/`, u.Scheme, u.Host),
})
if err != nil {
logger.Errorf(`failed to create access_token client. error: %s`, err.Error())
return ``, err
}
req, err := client.NewRequest(http.MethodPost, u.Path, &oAuthAccessTokenReq{
ClientID: clientID,
ClientSecret: clientSecret,
Code: code,
GrantType: `authorization_code`,
RedirectURI: redirectURI,
})
if err != nil {
logger.Errorf(`failed to create access_token request. error: %s`, err.Error())
return ``, err
}
atResp := new(oAuthAccessTokenResp)
resp, err := client.Do(req, atResp)
if err != nil {
logger.Errorf(`failed to request access_token. error: %s`, err.Error())
return ``, err
}
if resp.StatusCode >= 300 {
logger.Errorf(`access_token request return wrong code. code: %d, err_msg: %s`, resp.StatusCode, resp.Status)
return ``, err
}
return atResp.AccessToken, nil
}

61
util/open_id.go Normal file
View File

@ -0,0 +1,61 @@
package util
import (
"fmt"
"github.com/davidebianchi/go-jsonclient"
"net/http"
"net/url"
)
type openIdUserInfoResp struct {
Name string `json:"name"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
PreferredUsername string `json:"preferred_username"`
Email string `json:"email"`
Picture string `json:"picture"`
}
func OpenIdUserInfo(openIdUserInfoURL, accessToken string) (map[string]any, error) {
u, err := url.Parse(openIdUserInfoURL)
if err != nil {
logger.Errorf(`failed to parse openIdUserInfoURL. error: %s`, err.Error())
return nil, err
}
client, err := jsonclient.New(jsonclient.Options{
BaseURL: fmt.Sprintf(`%s://%s/`, u.Scheme, u.Host),
Headers: map[string]string{
`Authorization`: fmt.Sprintf(`Bearer %s`, accessToken),
},
})
if err != nil {
logger.Errorf(`failed to create user_info client. error: %s`, err.Error())
return nil, err
}
req, err := client.NewRequest(http.MethodGet, u.Path, nil)
if err != nil {
logger.Errorf(`failed to create user_info request. error: %s`, err.Error())
return nil, err
}
uiResp := new(openIdUserInfoResp)
resp, err := client.Do(req, uiResp)
if err != nil {
logger.Errorf(`failed to request user_info. error: %s`, err.Error())
return nil, err
}
if resp.StatusCode >= 300 {
logger.Errorf(`user_info request return wrong code. code: %d, err_msg: %s`, resp.StatusCode, resp.Status)
return nil, err
}
return map[string]any{
`userId`: uiResp.PreferredUsername,
`userName`: uiResp.Name,
`avatar`: uiResp.Picture,
}, nil
}

View File

@ -1,100 +0,0 @@
// 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 util
import (
"bytes"
"errors"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
)
type myos struct{}
// OS utilities.
var OS = myos{}
// IsWindows determines whether current OS is Windows.
func (*myos) IsWindows() bool {
return "windows" == runtime.GOOS
}
// Pwd gets the path of current working directory.
func (*myos) Pwd() string {
file, _ := exec.LookPath(os.Args[0])
pwd, _ := filepath.Abs(file)
return filepath.Dir(pwd)
}
// Home returns the home directory for the executing user.
//
// This uses an OS-specific method for discovering the home directory.
// An error is returned if a home directory cannot be detected.
func (*myos) Home() (string, error) {
user, err := user.Current()
if nil == err {
return user.HomeDir, nil
}
// cross compile support
if OS.IsWindows() {
return homeWindows()
}
// Unix-like system, so just assume Unix
return homeUnix()
}
func homeUnix() (string, error) {
// First prefer the HOME environmental variable
if home := os.Getenv("HOME"); home != "" {
return home, nil
}
// If that fails, try the shell
var stdout bytes.Buffer
cmd := exec.Command("sh", "-c", "eval echo ~$USER")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return "", err
}
result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}
return result, nil
}
func homeWindows() (string, error) {
drive := os.Getenv("HOMEDRIVE")
path := os.Getenv("HOMEPATH")
home := drive + path
if drive == "" || path == "" {
home = os.Getenv("USERPROFILE")
}
if home == "" {
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
}
return home, nil
}

View File

@ -1,49 +0,0 @@
// 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 util
import (
"runtime"
"testing"
)
func TestIsWiindows(t *testing.T) {
goos := runtime.GOOS
if "windows" == goos && !OS.IsWindows() {
t.Error("runtime.GOOS returns [windows]")
return
}
}
func TestPwd(t *testing.T) {
if "" == OS.Pwd() {
t.Error("Working directory should not be empty")
return
}
}
func TestHome(t *testing.T) {
home, err := OS.Home()
if nil != err {
t.Error("Can not get user home")
return
}
t.Log(home)
}

View File

@ -1,104 +0,0 @@
// 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 util
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"runtime"
"github.com/b3log/wide/log"
)
// Logger.
var logger = log.NewLogger(os.Stdout)
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
slash = []byte("/")
)
// Recover recovers a panic.
func Recover() {
if re := recover(); nil != re {
stack := stack()
logger.Errorf("PANIC RECOVERED: %v\n\t%s\n", re, stack)
}
}
// stack implements Stack, skipping 2 frames.
func stack() []byte {
buf := &bytes.Buffer{} // the returned data
// As we loop, we open files and read them. These variables record the currently
// loaded file.
var lines [][]byte
var lastFile string
for i := 2; ; i++ { // Caller we care about is the user, 2 frames up
pc, file, line, ok := runtime.Caller(i)
if !ok {
break
}
// Print this much at least. If we can't find the source, it won't show.
fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc)
if file != lastFile {
data, err := ioutil.ReadFile(file)
if err != nil {
continue
}
lines = bytes.Split(data, []byte{'\n'})
lastFile = file
}
line-- // in stack trace, lines are 1-indexed but our array is 0-indexed
fmt.Fprintf(buf, "\t%s: %s\n", function(pc), source(lines, line))
}
return buf.Bytes()
}
// source returns a space-trimmed slice of the n'th line.
func source(lines [][]byte, n int) []byte {
if n < 0 || n >= len(lines) {
return dunno
}
return bytes.Trim(lines[n], " \t")
}
// function returns, if possible, the name of the function containing the PC.
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
// Since the package path might contains dots (e.g. code.google.com/...),
// we first remove the path prefix if there is one.
if lastslash := bytes.LastIndex(name, slash); lastslash >= 0 {
name = name[lastslash+1:]
}
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
}
name = bytes.Replace(name, centerDot, dot, -1)
return name
}

View File

@ -1,23 +0,0 @@
// 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 util
import "testing"
func TestRecover(t *testing.T) {
defer Recover()
panic("test panic")
}

View File

@ -1,47 +0,0 @@
// 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 util
import (
"math/rand"
"time"
)
type myrand struct{}
// Random utilities.
var Rand = myrand{}
// String returns a random string ['a', 'z'] in the specified length
func (*myrand) String(length int) string {
bytes := make([]byte, length)
for i := 0; i < length; i++ {
bytes[i] = byte(Rand.Int('a', 'z'))
time.Sleep(100 * time.Nanosecond)
}
return string(bytes)
}
// Int returns a random integer in range [min, max].
func (*myrand) Int(min int, max int) int {
rand.Seed(time.Now().UnixNano())
time.Sleep(100 * time.Nanosecond)
return min + rand.Intn(max-min)
}

View File

@ -1,35 +0,0 @@
// 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 util
import "testing"
func TestString(t *testing.T) {
r1 := Rand.String(16)
r2 := Rand.String(16)
if r1 == r2 {
t.Fail()
}
}
func TestInt(t *testing.T) {
r1 := Rand.Int(0, 65535)
r2 := Rand.Int(0, 65535)
if r1 == r2 {
t.Fail()
}
}

View File

@ -1,115 +0,0 @@
// 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 util
import (
"compress/gzip"
"encoding/json"
"net/http"
"os"
"github.com/b3log/wide/log"
)
// Logger.
var retLogger = log.NewLogger(os.Stdout)
// Result.
type Result struct {
Succ bool `json:"succ"` // successful or not
Code int `json:"code"` // return code
Msg string `json:"msg"` // message
Data interface{} `json:"data"` // data object
}
// NewResult creates a result with Succ=true, Code=0, Msg="", Data=nil.
func NewResult() *Result {
return &Result{
Succ: true,
Code: 0,
Msg: "",
Data: nil,
}
}
// RetResult writes HTTP response with "Content-Type, application/json".
func RetResult(w http.ResponseWriter, r *http.Request, res *Result) {
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(res)
if err != nil {
retLogger.Error(err)
return
}
w.Write(data)
}
// RetGzResult writes HTTP response with "Content-Type, application/json" and "Content-Encoding, gzip".
func RetGzResult(w http.ResponseWriter, r *http.Request, res *Result) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
err := json.NewEncoder(gz).Encode(res)
if nil != err {
retLogger.Error(err)
return
}
err = gz.Close()
if nil != err {
retLogger.Error(err)
return
}
}
// RetJSON writes HTTP response with "Content-Type, application/json".
func RetJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) {
w.Header().Set("Content-Type", "application/json")
data, err := json.Marshal(res)
if err != nil {
retLogger.Error(err)
return
}
w.Write(data)
}
// RetGzJSON writes HTTP response with "Content-Type, application/json" and "Content-Encoding, gzip".
func RetGzJSON(w http.ResponseWriter, r *http.Request, res map[string]interface{}) {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Encoding", "gzip")
gz := gzip.NewWriter(w)
err := json.NewEncoder(gz).Encode(res)
if nil != err {
retLogger.Error(err)
return
}
err = gz.Close()
if nil != err {
retLogger.Error(err)
return
}
}

View File

@ -1,61 +0,0 @@
// 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 util
type str struct{}
// String utilities.
var Str = str{}
// Contains determines whether the str is in the strs.
func (*str) Contains(str string, strs []string) bool {
for _, v := range strs {
if v == str {
return true
}
}
return false
}
// LCS gets the longest common substring of s1 and s2.
//
// Refers to http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_substring.
func (*str) LCS(s1 string, s2 string) string {
var m = make([][]int, 1+len(s1))
for i := 0; i < len(m); i++ {
m[i] = make([]int, 1+len(s2))
}
longest := 0
xLongest := 0
for x := 1; x < 1+len(s1); x++ {
for y := 1; y < 1+len(s2); y++ {
if s1[x-1] == s2[y-1] {
m[x][y] = m[x-1][y-1] + 1
if m[x][y] > longest {
longest = m[x][y]
xLongest = x
}
} else {
m[x][y] = 0
}
}
}
return s1[xLongest-longest : xLongest]
}

View File

@ -1,35 +0,0 @@
// 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 util
import "testing"
func TestContains(t *testing.T) {
if !Str.Contains("123", []string{"123", "345"}) {
t.Error("[\"123\", \"345\"] should contain \"123\"")
return
}
}
func TestLCS(t *testing.T) {
str := Str.LCS("123456", "abc34def")
if "34" != str {
t.Error("[\"123456\"] and [\"abc34def\"] should have the longest common substring [\"34\"]")
return
}
}

View File

@ -1,230 +0,0 @@
// 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 util
import (
"archive/zip"
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"unicode/utf8"
"golang.org/x/text/encoding/simplifiedchinese"
"golang.org/x/text/transform"
)
type myzip struct{}
// Zip utilities.
var Zip = myzip{}
// ZipFile represents a zip file.
type ZipFile struct {
zipFile *os.File
writer *zip.Writer
}
// Create creates a zip file with the specified filename.
func (*myzip) Create(filename string) (*ZipFile, error) {
file, err := os.Create(filename)
if nil != err {
return nil, err
}
return &ZipFile{zipFile: file, writer: zip.NewWriter(file)}, nil
}
// Close closes the zip file writer.
func (z *ZipFile) Close() error {
err := z.writer.Close()
if nil != err {
return err
}
return z.zipFile.Close() // close the underlying writer
}
// AddEntryN adds entries.
func (z *ZipFile) AddEntryN(path string, names ...string) error {
for _, name := range names {
zipPath := filepath.Join(path, name)
err := z.AddEntry(zipPath, name)
if nil != err {
return err
}
}
return nil
}
// AddEntry adds a entry.
func (z *ZipFile) AddEntry(path, name string) error {
fi, err := os.Stat(name)
if nil != err {
return err
}
fh, err := zip.FileInfoHeader(fi)
if nil != err {
return err
}
fh.Name = filepath.ToSlash(filepath.Clean(path))
fh.Method = zip.Deflate // data compression algorithm
if fi.IsDir() {
fh.Name = fh.Name + "/" // be care the ending separator
}
entry, err := z.writer.CreateHeader(fh)
if nil != err {
return err
}
if fi.IsDir() {
return nil
}
file, err := os.Open(name)
if nil != err {
return err
}
defer file.Close()
_, err = io.Copy(entry, file)
return err
}
// AddDirectoryN adds directories.
func (z *ZipFile) AddDirectoryN(path string, names ...string) error {
for _, name := range names {
err := z.AddDirectory(path, name)
if nil != err {
return err
}
}
return nil
}
// AddDirectory adds a directory.
func (z *ZipFile) AddDirectory(path, dirName string) error {
files, err := ioutil.ReadDir(dirName)
if nil != err {
return err
}
if 0 == len(files) {
err := z.AddEntry(path, dirName)
if nil != err {
return err
}
return nil
}
for _, file := range files {
localPath := filepath.Join(dirName, file.Name())
zipPath := filepath.Join(path, file.Name())
err = nil
if file.IsDir() {
err = z.AddDirectory(zipPath, localPath)
} else {
err = z.AddEntry(zipPath, localPath)
}
if nil != err {
return err
}
}
return nil
}
func cloneZipItem(f *zip.File, dest string) error {
// create full directory path
fileName := f.Name
if !utf8.ValidString(fileName) {
data, err := ioutil.ReadAll(transform.NewReader(bytes.NewReader([]byte(fileName)), simplifiedchinese.GB18030.NewDecoder()))
if nil == err {
fileName = string(data)
} else {
logger.Error(err)
}
}
path := filepath.Join(dest, fileName)
err := os.MkdirAll(filepath.Dir(path), os.ModeDir|os.ModePerm)
if nil != err {
return err
}
if f.FileInfo().IsDir() {
err = os.Mkdir(path, os.ModeDir|os.ModePerm)
if nil != err {
return err
}
return nil
}
// clone if item is a file
rc, err := f.Open()
if nil != err {
return err
}
defer rc.Close()
// use os.Create() since Zip don't store file permissions
fileCopy, err := os.Create(path)
if nil != err {
return err
}
defer fileCopy.Close()
_, err = io.Copy(fileCopy, rc)
if nil != err {
return err
}
return nil
}
// Unzip extracts a zip file specified by the zipFilePath to the destination.
func (*myzip) Unzip(zipFilePath, destination string) error {
r, err := zip.OpenReader(zipFilePath)
if nil != err {
return err
}
defer r.Close()
for _, f := range r.File {
err = cloneZipItem(f, destination)
if nil != err {
return err
}
}
return nil
}

View File

@ -1,149 +0,0 @@
// 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 util
import (
"os"
"path/filepath"
"testing"
)
var testDir = "../tmp"
var packageName = filepath.Join(testDir, "test_zip")
func TestCreate(t *testing.T) {
zipFile, err := Zip.Create(packageName + ".zip")
if nil != err {
t.Error(err)
return
}
zipFile.AddDirectoryN(".", ".")
if nil != err {
t.Error(err)
return
}
err = zipFile.Close()
if nil != err {
t.Error(err)
return
}
}
func TestUnzip(t *testing.T) {
err := Zip.Unzip(packageName+".zip", packageName)
if nil != err {
t.Error(err)
return
}
}
func _TestEmptyDir(t *testing.T) {
dir1 := "/dir/subDir1"
dir2 := "/dir/subDir2"
err := os.MkdirAll(packageName+dir1, os.ModeDir)
if nil != err {
t.Error(err)
return
}
err = os.MkdirAll(packageName+dir2, os.ModeDir)
if nil != err {
t.Error(err)
return
}
f, err := os.Create(packageName + dir2 + "/file")
if nil != err {
t.Error(err)
return
}
f.Close()
zipFile, err := Zip.Create(packageName + "/dir.zip")
if nil != err {
t.Error(err)
return
}
zipFile.AddDirectoryN("dir", packageName+"/dir")
if nil != err {
t.Error(err)
return
}
err = zipFile.Close()
if nil != err {
t.Error(err)
return
}
err = Zip.Unzip(packageName+"/dir.zip", packageName+"/unzipDir")
if nil != err {
t.Error(err)
return
}
if !File.IsExist(packageName+"/unzipDir") || !File.IsDir(packageName+"/unzipDir") {
t.Error("Unzip failed")
return
}
if !File.IsExist(packageName+"/unzipDir"+dir1) || !File.IsDir(packageName+"/unzipDir"+dir1) {
t.Error("Unzip failed")
return
}
if !File.IsExist(packageName+"/unzipDir"+dir2) || !File.IsDir(packageName+"/unzipDir"+dir2) {
t.Error("Unzip failed")
return
}
if !File.IsExist(packageName+"/unzipDir"+dir2+"/file") || File.IsDir(packageName+"/unzipDir"+dir2+"/file") {
t.Error("Unzip failed")
return
}
}
func TestMain(m *testing.M) {
logger.Info(testDir)
retCode := m.Run()
// clean test data
os.RemoveAll(testDir + "/test_zip")
os.RemoveAll(testDir + "/util")
os.RemoveAll(testDir + "/file.go")
os.RemoveAll(testDir + "/test_zip.zip")
os.Exit(retCode)
}

View File

@ -15,7 +15,7 @@
<div class="item">
<label>{{.i18n.project_address}}{{.i18n.colon}}</label>
<a href="https://github.com/b3log/wide" target="_blank">github.com/b3log/wide</a><br/>
<a href="https://github.com/88250/wide" target="_blank">github.com/88250/wide</a><br/>
<label>{{.i18n.dev_team}}{{.i18n.colon}}</label>
<a href="https://github.com/b3log" target="_blank">B3log</a><br/>
@ -35,7 +35,7 @@
<a href="https://github.com/marijnh/CodeMirror" target="_blank">CodeMirror</a>
<a href="https://github.com/zTree/zTree_v3" target="_blank">zTree</a>
<a href="https://github.com/visualfc/liteide" target="_blank">LiteIDE</a>
<a href="https://github.com/nsf/gocode" target="_blank">gocode</a>
<a href="https://github.com/stamblerre/gocode" target="_blank">gocode</a>
<a href="https://github.com/gorilla" target="_blank">Gorilla</a>
<a href="https://docker.com" target="_blank">Docker</a>
</li>
@ -45,7 +45,7 @@
<div class="space"></div>
<div class="item">
<label><a href="https://github.com/b3log/wide/blob/master/TERMS.md" target="_blank">{{.i18n.terms}}</a> & {{.i18n.license}}{{.i18n.colon}}</label>
<label><a href="https://github.com/88250/wide/blob/master/TERMS.md" target="_blank">{{.i18n.terms}}</a> & {{.i18n.license}}{{.i18n.colon}}</label>
<pre class="license">
Apache License
Version 2.0, January 2004

View File

@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<title>{{.i18n.wide}} - {{.i18n.wide_title}}</title>
<meta name="keywords" content="Wide, Golang, IDE, Team, Cloud, B3log"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywrhere."/>
<meta name="keywords" content="Wide, Golang, IDE, Cloud, B3log"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anwhere."/>
<meta name="author" content="B3log">
<meta property="og:description" content="A Web-based Go IDE, do your development anytime, anywhere."/>
{{if eq $.conf.RuntimeMode "dev"}}
@ -31,6 +31,9 @@
<link rel="stylesheet" href="/static/css/themes/{{.user.Theme}}.css?{{.conf.StaticResourceVersion}}" id="themesLink">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
{{if ne "" .conf.SiteStatCode}}
{{.conf.SiteStatCode}}
{{end}}
</head>
<body>
<!-- menu bar -->
@ -277,15 +280,10 @@
<span>{{.i18n.test}}</span>
</li>
<li class="hr"></li>
<li class="go-mod disabled" onclick="if (!$(this).hasClass('disabled')){menu.gomod()}">
<span class="space"></span>
<span>{{.i18n.gomod}}</span>
</li>
<li class="go-install disabled" onclick="if (!$(this).hasClass('disabled')){menu.goinstall()}">
<span class="space"></span>
<span>{{.i18n.goinstall}}</span>
</li>
<li class="hr"></li>
<li class="go-vet disabled" onclick="if (!$(this).hasClass('disabled')){menu.govet()}">
<span class="space"></span>
<span>{{.i18n.govet}}</span>
@ -300,11 +298,11 @@
<span>{{.i18n.help}}</span>
<div class="frame">
<ul>
<li onclick="window.open('https://hacpai.com/article/1538873544275')">
<li onclick="window.open('https://ld246.com/article/1538873544275')">
<span class="font-ico ico-book"></span>
<span>{{.i18n.wide_doc}}</span>
</li>
<li onclick="window.open('https://github.com/b3log/wide/issues/new/choose')">
<li onclick="window.open('https://github.com/88250/wide/issues/new/choose')">
<span class="ico-report font-ico"></span>
{{.i18n.issues}}
</li>
@ -327,6 +325,10 @@
<span class="font-ico ico-about"></span>
<span>{{.i18n.about}}</span>
</li>
<li onclick="window.open('https://ld246.com/sponsor')">
<span class="space"></span>
<span>{{.i18n.sponsor}}</span>
</li>
</ul>
</div>
</li>
@ -340,7 +342,7 @@
src="{{.user.Avatar}}"
title="{{.user.Name}}"/>
<span class="font-ico ico-share"></span> &nbsp;
<span onclick="window.open('https://github.com/b3log/wide')"
<span onclick="window.open('https://github.com/88250/wide')"
class="font-ico ico-github"></span>&nbsp;
<div class="share-panel frame">
<span title="Email" class="font-ico ico-email"></span>

View File

@ -4,11 +4,14 @@
<meta charset="UTF-8">
<title>{{.i18n.wide}} - {{.i18n.keyboard_shortcuts}}</title>
<meta name="keywords" content="Wide, Golang, IDE, Team, Cloud, B3log, Keyboard Shortcuts"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywrhere."/>
<meta name="keywords" content="Wide, Golang, IDE, Cloud, B3log, Keyboard Shortcuts"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywhere."/>
<meta name="author" content="B3log">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
{{if ne "" .conf.SiteStatCode}}
{{.conf.SiteStatCode}}
{{end}}
</head>
<body>
<h2>{{.i18n.editor}}</h2>

View File

@ -4,24 +4,27 @@
<meta charset="UTF-8">
<title>{{.i18n.wide}} - {{.i18n.wide_title}}</title>
<meta name="keywords" content="Wide, Golang, IDE, Team, Cloud, B3log, Login"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywrhere."/>
<meta name="keywords" content="Wide, Golang, IDE, Cloud, B3log, Login"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywhere."/>
<meta name="author" content="B3log">
<link rel="stylesheet" href="/static/css/base.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="/static/css/sign.css?{{.conf.StaticResourceVersion}}">
<link rel="icon" type="image/x-icon" href="/favicon.ico"/>
{{if ne "" .conf.SiteStatCode}}
{{.conf.SiteStatCode}}
{{end}}
</head>
<body>
<div class="header">
<div class="wrapper fn-clear">
<a href="/login" rel="login" style="flex:1">
<a href="{{$.conf.Server}}/login" rel="login" style="flex:1">
<img title="A Web-based Go IDE" src="/static/images/wide-logo.png" class="logo"/></a>
<ul>
<li><a href="/playground" target="_blank" style="color: #cd504a">Play</a></li>
<li><a rel="bookmark" href="https://github.com/b3log/wide" target="_blank">GitHub</a></li>
<li><a rel="help" href="https://hacpai.com/article/1538873544275" target="_blank">{{.i18n.help}}</a></li>
<li><a rel="bookmark" href="https://hacpai.com" target="_blank">{{.i18n.community}}</a></li>
<li><a href="{{$.conf.Server}}/playground" target="_blank" style="color: #cd504a">Play</a></li>
<li><a rel="bookmark" href="https://github.com/88250/wide" target="_blank">GitHub</a></li>
<li><a rel="help" href="https://ld246.com/article/1538873544275" target="_blank">{{.i18n.help}}</a></li>
<li><a rel="bookmark" href="https://ld246.com" target="_blank">{{.i18n.community}}</a></li>
</ul>
</div>
</div>
@ -31,19 +34,23 @@
<h2>Hello, 世界</h2>
<h3>Coding with Go on the Wide way.</h3>
</div>
<div class="form fn-right">
<div style="text-align: center"><a href="https://hacpai.com/article/1558097702072" target="_blank">2019-05-17 关于账号迁移的公告</a></div>
<div class="login__github oauth"></div>
<img style="display: none" src="/static/images/github.gif"/>
<button class="btn oauth">登录 GitHub 账号后即可开始使用</button>
<div class="desc">
<label class="checkbox">
<input type="checkbox"/>
是否愿意在 GitHub 上收藏该<a href="https://github.com/b3log/wide" target="_blank">项目</a>、关注<a
href="https://github.com/88250" target="_blank">开发者</a>并加入 <a
href="https://github.com/b3log" target="_blank">B3log
开源组织</a>
</label>
<div class="form fn-right start">
<a href="{{$.conf.Server}}/login/redirect">
<svg class="login__icon" viewBox="0 0 32 32">
<path fill="#d23f31" style="fill: var(--color1, #d23f31)"
d="M5.787 17.226h17.033l5.954 9.528c0.47 0.752 0.003 1.361-1.042 1.361h-15.141z"></path>
<path d="M10.74 3.927h17.033c1.045 0 1.512 0.609 1.042 1.361l-5.954 9.528h-19.872l6.379-10.209c0.235-0.376 0.849-0.681 1.372-0.681z"></path>
<path d="M2.953 17.226h2.839l6.804 10.889h-1.892c-0.523 0-1.137-0.305-1.372-0.681z"></path>
</svg>
</a>
<div class="start__aciton">
<a href="{{$.conf.Server}}/login/redirect" class="btn">登录链滴社区账号后即可开始使用</a>
<span>&nbsp; &nbsp;</span>
<a href="https://ld246.com/article/1576294445994" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 32 32">
<path d="M19.652 25v6c0 0.55-0.45 1-1 1h-6c-0.55 0-1-0.45-1-1v-6c0-0.55 0.45-1 1-1h6c0.55 0 1 0.45 1 1zM27.552 10c0 4.75-3.225 6.575-5.6 7.9-1.475 0.85-2.4 2.575-2.4 3.3v0c0 0.55-0.425 1.2-1 1.2h-6c-0.55 0-0.9-0.85-0.9-1.4v-1.125c0-3.025 3-5.625 5.2-6.625 1.925-0.875 2.725-1.7 2.725-3.3 0-1.4-1.825-2.65-3.85-2.65-1.125 0-2.15 0.35-2.7 0.725-0.6 0.425-1.2 1.025-2.675 2.875-0.2 0.25-0.5 0.4-0.775 0.4-0.225 0-0.425-0.075-0.625-0.2l-4.1-3.125c-0.425-0.325-0.525-0.875-0.25-1.325 2.7-4.475 6.5-6.65 11.6-6.65 5.35 0 11.35 4.275 11.35 10z"></path>
</svg>
</a>
</div>
</div>
</div>
@ -53,11 +60,5 @@
Ver {{.ver}}, &copy; {{.year}}&nbsp;<a rel="copyright" href="https://b3log.org" target="_blank">B3log 开源</a>
</span>
</div>
<script type="text/javascript" src="/static/js/lib/jquery-2.1.1.min.js"></script>
<script>
$('.oauth').click(function () {
window.location.href = '/oauth/github/redirect?state=' + ($('input').prop('checked') ? '0' : '1')
})
</script>
</body>
</html>

View File

@ -1,18 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<head>
<meta charset="UTF-8">
<title>{{.i18n.wide}} - {{.i18n.wide_title}}</title>
<meta name="keywords" content="Wide, Golang, IDE, Team, Cloud, B3log, Playground"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywrhere."/>
<meta name="keywords" content="Wide, Golang, IDE, Cloud, B3log, Playground"/>
<meta name="description" content="A Web-based Go IDE , do your development anytime, anywhere."/>
<meta name="author" content="B3log">
<link rel="stylesheet" href="/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet"
href="/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/show-hint.css">
<link rel="stylesheet" href="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldgutter.css">
<link rel="stylesheet" href="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/dialog/dialog.css">
<link rel="stylesheet" href="{{$.conf.Server}}/static/js/overwrite/codemirror/theme/wide.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet"
href="{{$.conf.Server}}/static/js/overwrite/codemirror/theme/wide.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="/static/css/dialog.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="/static/css/base.css?{{.conf.StaticResourceVersion}}">
@ -23,10 +25,13 @@
<link rel="stylesheet" href="/static/css/side.css?{{.conf.StaticResourceVersion}}">
<link rel="stylesheet" href="/static/css/playground.css?{{.conf.StaticResourceVersion}}">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
</head>
<body>
<div class="header menu fn-clear">
<link rel="icon" type="image/x-icon" href="/favicon.ico"/>
{{if ne "" .conf.SiteStatCode}}
{{.conf.SiteStatCode}}
{{end}}
</head>
<body>
<div class="header menu fn-clear">
<ul class="fn-left">
<li>
<a href="/" target="_blank">
@ -44,11 +49,16 @@
<li>
<button class="btn-white btn" id="share" onclick="playground.share();">{{.i18n.share}}</button>
</li>
<li>
<button class="btn btn-blue" onclick="window.open('https://ld246.com/sponsor')">
{{.i18n.sponsor}}
</button>
</li>
</ul>
<div class="fn-right">
<span class="font-ico ico-about" onclick='$("#dialogAbout").dialog("open");'></span> &nbsp;
<span class="font-ico ico-share"></span> &nbsp;
<span onclick="window.open('https://github.com/b3log/wide')"
<span onclick="window.open('https://github.com/88250/wide')"
class="font-ico ico-github"></span>&nbsp;
<div class="share-panel frame" style="display: none;">
<span title="Email" class="font-ico ico-email"></span>
@ -58,9 +68,9 @@
<span title="QQ空间" class="font-ico ico-qqz"></span>
</div>
</div>
</div>
<div class="fn-clear">
<div class="fn-left" id="editorDivWrap">
</div>
<div class="main">
<div id="editorDivWrap">
<div id="editorDiv">
<textarea rows="20" id='editor' class="fn-none">{{.code}}</textarea>
</div>
@ -68,17 +78,18 @@
<div id="output" class="output"></div>
</div>
</div>
<div class="fn-right" id="goNews"></div>
</div>
<div class="footer">
<div id="goNews"></div>
</div>
<div class="footer">
<span class="wrapper">
Ver {{.ver}}, &copy; {{.year}}&nbsp;<a rel="copyright" href="https://b3log.org" target="_blank">B3log 开源</a>
Ver {{.ver}}, &copy; {{.year}}&nbsp;<a rel="copyright" href="https://b3log.org"
target="_blank">B3log 开源</a>
</span>
</div>
</div>
<div id="dialogAbout" class="fn-none"></div>
<div id="dialogShare" class="fn-none"></div>
<script>
<div id="dialogAbout" class="fn-none"></div>
<div id="dialogShare" class="fn-none"></div>
<script>
var channelScheme = -1 < window.location.protocol.indexOf("https") ? "wss":"ws";
var channel = channelScheme + "://" + window.location.hostname + ":" + window.location.port;
var config = {
@ -93,35 +104,36 @@
};
return ret;
}
</script>
</script>
<script type="text/javascript" src="/static/js/lib/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="/static/js/lib/reconnecting-websocket.js"></script>
<script type="text/javascript" src="/static/js/lib/Autolinker.min.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.min.js"></script>
<script type="text/javascript"
src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/selection/active-line.js"></script>
<script type="text/javascript" src="/static/js/overwrite/codemirror/addon/hint/show-hint.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/anyword-hint.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/display/rulers.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closebrackets.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/matchbrackets.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closetag.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/searchcursor.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/search.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/dialog/dialog.js"></script>
<script type="text/javascript"
src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/match-highlighter.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldcode.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldgutter.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/brace-fold.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/comment-fold.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/mode/loadmode.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/comment/comment.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/meta.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/go/go.js"></script>
<script type="text/javascript" src="/static/js/lib/jquery-2.1.1.min.js"></script>
<script type="text/javascript" src="/static/js/lib/reconnecting-websocket.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/codemirror.min.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/selection/active-line.js"></script>
<script type="text/javascript" src="/static/js/overwrite/codemirror/addon/hint/show-hint.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/hint/anyword-hint.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/display/rulers.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closebrackets.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/matchbrackets.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/edit/closetag.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/searchcursor.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/search.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/dialog/dialog.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/search/match-highlighter.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldcode.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/foldgutter.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/brace-fold.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/fold/comment-fold.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/mode/loadmode.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/addon/comment/comment.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/meta.js"></script>
<script type="text/javascript" src="/static/js/lib/codemirror-{{.codeMirrorVer}}/mode/go/go.js"></script>
<script type="text/javascript" src="/static/js/dialog.js?{{.conf.StaticResourceVersion}}"></script>
<script type="text/javascript" src="/static/js/menu.js?{{.conf.StaticResourceVersion}}"></script>
<script type="text/javascript" src="/static/js/playground.js?{{.conf.StaticResourceVersion}}"></script>
</body>
<script type="text/javascript" src="/static/js/dialog.js?{{.conf.StaticResourceVersion}}"></script>
<script type="text/javascript" src="/static/js/menu.js?{{.conf.StaticResourceVersion}}"></script>
<script type="text/javascript" src="/static/js/playground.js?{{.conf.StaticResourceVersion}}"></script>
</body>
</html>

View File

@ -14,14 +14,14 @@
<span>{{.workspace}}</span>
</li>
<li class="border">
<label><a href="https://hacpai.com/article/1538873544275" target="_blank">{{.i18n.user_guide}}</a></label>&nbsp;&nbsp;&nbsp;&nbsp;&amp;&nbsp;&nbsp;&nbsp;
<label><a href="https://hacpai.com/article/1538876422995" target="_blank">{{.i18n.dev_guide}}</a></label>
<label><a href="https://ld246.com/article/1538873544275" target="_blank">{{.i18n.user_guide}}</a></label>&nbsp;&nbsp;&nbsp;&nbsp;&amp;&nbsp;&nbsp;&nbsp;
<label><a href="https://ld246.com/article/1538876422995" target="_blank">{{.i18n.dev_guide}}</a></label>
</li>
<li>
<label>{{.i18n.current_ver}}{{.i18n.colon}}</label>
{{.ver}}<br/>
<label>{{.i18n.project_address}}{{.i18n.colon}}</label>
<a href="https://github.com/b3log/wide" target="_blank">github.com/b3log/wide</a><br/>
<a href="https://github.com/88250/wide" target="_blank">github.com/88250/wide</a><br/>
<label>{{.i18n.dev_team}}{{.i18n.colon}}</label>
<a href="https://github.com/b3log/b3log-solo/wiki/About_us" target="_blank">B3log</a>
</li>

1341
yarn.lock

File diff suppressed because it is too large Load Diff