├── README.md
├── static
├── imgs
│ ├── books
│ │ ├── xsw.jpg
│ │ ├── caoda.jpg
│ │ ├── goweb.jpg
│ │ ├── xuhen.jpg
│ │ ├── huangjh.jpg
│ │ ├── go-in-action.jpg
│ │ ├── go-programming-blueprints.png
│ │ ├── mastering-go-web-services.jpg
│ │ ├── go-programming-language-book.png
│ │ ├── building-microservices-with-go.jpg
│ │ ├── building-restful-web-services-with-go.jpg
│ │ ├── powerful-command-line-applications-in-go.jpg
│ │ └── hands-on-software-architecture-with-golang.jpg
│ ├── wx_qrcode.jpg
│ ├── logos
│ │ ├── etcd.png
│ │ ├── jrtt.png
│ │ ├── tidb.png
│ │ ├── caddy.jpeg
│ │ ├── prometheus.png
│ │ ├── netflix.svg
│ │ ├── cloudflare-icon.svg
│ │ ├── the-new-york-times-icon.svg
│ │ ├── mongodb.svg
│ │ ├── uber-app-icon.svg
│ │ ├── dropbox.svg
│ │ ├── github.svg
│ │ ├── uber.svg
│ │ ├── kubernetes.svg
│ │ ├── twitch.svg
│ │ ├── capital-one.svg
│ │ ├── stripe.svg
│ │ ├── google-cloud.svg
│ │ ├── didi.svg
│ │ ├── comcast.svg
│ │ └── mercadoLibre.svg
│ ├── studygolang-white.png
│ ├── solutions
│ │ ├── americanexpress-logo.png
│ │ ├── twitter-logo.svg
│ │ ├── netflix-logo.svg
│ │ ├── CLI-green.svg
│ │ ├── webdev-green.svg
│ │ ├── target-logo.svg
│ │ ├── uber-logo.svg
│ │ ├── dropbox-logo.svg
│ │ ├── twitch-logo.svg
│ │ ├── capital-one-logo.svg
│ │ ├── ops-green.svg
│ │ ├── cloud-green.svg
│ │ └── mercadolibre-logo.svg
│ ├── menu-24px.svg
│ ├── menu-24px-white.svg
│ ├── close-24px.svg
│ ├── star-24px.svg
│ ├── quote.svg
│ ├── go-logo-blue.svg
│ ├── go-logo-white.svg
│ ├── pink.svg
│ ├── gophers
│ │ ├── peach.svg
│ │ └── happy.svg
│ └── pilot-bust.svg
└── js
│ └── base.js
├── template
├── gopher.html
├── about.html
├── common
│ └── layout.html
├── solutions
│ └── webdev.html
└── solution.html
├── go.mod
├── .github
└── CODEOWNERS
├── .gitignore
├── config
└── config.toml
├── Makefile
├── http
├── controller
│ ├── routes.go
│ ├── repo.go
│ └── index.go
├── http.go
└── funcs.go
├── main.go
├── util
└── file.go
├── LICENSE
├── global
├── init.go
└── app.go
└── .air.conf
/README.md:
--------------------------------------------------------------------------------
1 | # golangclub
2 | Go语言俱乐部
3 |
--------------------------------------------------------------------------------
/static/imgs/books/xsw.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/xsw.jpg
--------------------------------------------------------------------------------
/static/imgs/wx_qrcode.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/wx_qrcode.jpg
--------------------------------------------------------------------------------
/static/imgs/books/caoda.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/caoda.jpg
--------------------------------------------------------------------------------
/static/imgs/books/goweb.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/goweb.jpg
--------------------------------------------------------------------------------
/static/imgs/books/xuhen.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/xuhen.jpg
--------------------------------------------------------------------------------
/static/imgs/logos/etcd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/logos/etcd.png
--------------------------------------------------------------------------------
/static/imgs/logos/jrtt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/logos/jrtt.png
--------------------------------------------------------------------------------
/static/imgs/logos/tidb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/logos/tidb.png
--------------------------------------------------------------------------------
/static/imgs/books/huangjh.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/huangjh.jpg
--------------------------------------------------------------------------------
/static/imgs/logos/caddy.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/logos/caddy.jpeg
--------------------------------------------------------------------------------
/static/imgs/logos/prometheus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/logos/prometheus.png
--------------------------------------------------------------------------------
/static/imgs/books/go-in-action.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/go-in-action.jpg
--------------------------------------------------------------------------------
/static/imgs/studygolang-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/studygolang-white.png
--------------------------------------------------------------------------------
/static/imgs/books/go-programming-blueprints.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/go-programming-blueprints.png
--------------------------------------------------------------------------------
/static/imgs/books/mastering-go-web-services.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/mastering-go-web-services.jpg
--------------------------------------------------------------------------------
/static/imgs/solutions/americanexpress-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/solutions/americanexpress-logo.png
--------------------------------------------------------------------------------
/template/gopher.html:
--------------------------------------------------------------------------------
1 | {{define "title"}}Gopher名人 — Go语言俱乐部{{end}}
2 | {{define "content"}}
3 |
6 | {{end}}
--------------------------------------------------------------------------------
/static/imgs/books/go-programming-language-book.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/go-programming-language-book.png
--------------------------------------------------------------------------------
/static/imgs/books/building-microservices-with-go.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/building-microservices-with-go.jpg
--------------------------------------------------------------------------------
/static/imgs/books/building-restful-web-services-with-go.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/building-restful-web-services-with-go.jpg
--------------------------------------------------------------------------------
/static/imgs/books/powerful-command-line-applications-in-go.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/powerful-command-line-applications-in-go.jpg
--------------------------------------------------------------------------------
/static/imgs/books/hands-on-software-architecture-with-golang.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/polaris1119/golangclub/HEAD/static/imgs/books/hands-on-software-architecture-with-golang.jpg
--------------------------------------------------------------------------------
/static/imgs/menu-24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/polaris1119/golangclub
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/labstack/echo/v4 v4.9.0
7 | github.com/labstack/gommon v0.3.1
8 | github.com/spf13/viper v1.5.0
9 | )
10 |
--------------------------------------------------------------------------------
/static/imgs/menu-24px-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/close-24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/star-24px.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Lines starting with '#' are comments.
2 | # Each line is a file pattern followed by one or more owners.
3 | # https://help.github.com/articles/about-codeowners/
4 | #
5 | # Order is important; the last matching pattern takes the most
6 | # precedence.
7 |
8 | * @polaris1119 @unknwon
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, build with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | .idea
15 |
16 | tmp
17 |
18 | golangclub
19 |
20 | vendor
21 | .vscode
22 | .DS_Store
23 |
--------------------------------------------------------------------------------
/config/config.toml:
--------------------------------------------------------------------------------
1 |
2 | # 域名
3 | domain = "https://golangclub.com/"
4 | name = "Go语言俱乐部"
5 |
6 | # 定义 HTTP 监听端口
7 | [http]
8 | host = ""
9 | port = 2019
10 |
11 | # 存储配置
12 | [storage]
13 | driver = "mysql"
14 | user = "root"
15 | password = ""
16 | host = "localhost"
17 | port = 3306
18 | dbname = "golangclub"
19 | charset = "utf8mb4"
20 |
21 | [seo]
22 | keywords = "go.dev golangclub"
23 | description = "索引"
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | GitCommitLog=`git log --pretty=oneline -n 1`
2 | GitRelease=`git describe --dirty --all`
3 | BuildTime=`date '+%Y-%m-%d %H:%M:%S'`
4 | LdFlags="-X 'main.gitCommitLog=${GitCommitLog}' -X 'main.gitRelease=${GitRelease}' -X 'main.buildTime=${BuildTime}'"
5 |
6 | build: fmt
7 | @echo "building project..."
8 | go build -ldflags ${LdFlags} github.com/polaris1119/golangclub
9 | @echo "build finished!"
10 |
11 | fmt:
12 | gofmt -w .
--------------------------------------------------------------------------------
/http/controller/routes.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package controller
10 |
11 | import "github.com/labstack/echo/v4"
12 |
13 | func RegisterRoutes(e *echo.Echo) {
14 | new(IndexController).RegisterRoutes(e)
15 | new(RepoController).RegisterRoutes(e)
16 | }
17 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/labstack/echo/v4"
5 | "github.com/labstack/echo/v4/middleware"
6 |
7 | "github.com/polaris1119/golangclub/http/controller"
8 | )
9 |
10 | func main() {
11 | e := echo.New()
12 |
13 | e.Use(middleware.Recover())
14 | e.Use(middleware.Logger())
15 |
16 | // 去除尾部斜杠
17 | e.Pre(middleware.RemoveTrailingSlash())
18 |
19 | // 服务静态文件
20 | e.Static("/static", "static")
21 |
22 | controller.RegisterRoutes(e)
23 |
24 | e.Logger.Fatal(e.Start(":2019"))
25 | }
26 |
--------------------------------------------------------------------------------
/util/file.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package util
10 |
11 | import "os"
12 |
13 | // Exist 检查文件或目录是否存在
14 | // 如果由 filename 指定的文件或目录存在则返回 true,否则返回 false
15 | func Exist(filename string) bool {
16 | _, err := os.Stat(filename)
17 | return err == nil || os.IsExist(err)
18 | }
19 |
--------------------------------------------------------------------------------
/http/controller/repo.go:
--------------------------------------------------------------------------------
1 | package controller
2 |
3 | import (
4 | "bytes"
5 | "os/exec"
6 |
7 | "github.com/labstack/echo/v4"
8 | "github.com/labstack/gommon/log"
9 |
10 | "github.com/polaris1119/golangclub/global"
11 | )
12 |
13 | type RepoController struct{}
14 |
15 | func (r RepoController) RegisterRoutes(e *echo.Echo) {
16 | e.POST("/repo/pull", r.pull)
17 | }
18 |
19 | // pull 自动拉去仓库最新代码
20 | func (r RepoController) pull(ctx echo.Context) error {
21 | secret := "L072uFhwQ6"
22 | _ = secret
23 |
24 | strCmd := "cd " + global.App.RootDir + "; git pull"
25 | cmd := exec.Command("sh", "-c", strCmd)
26 | var out bytes.Buffer
27 | cmd.Stdout = &out
28 | err := cmd.Run()
29 |
30 | ctx.Logger().Infoj(log.JSON{"pull_result": out.String()})
31 |
32 | return err
33 | }
34 |
--------------------------------------------------------------------------------
/static/imgs/solutions/twitter-logo.svg:
--------------------------------------------------------------------------------
1 | Twitter_Logo_Blue
--------------------------------------------------------------------------------
/static/imgs/quote.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/static/imgs/logos/netflix.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/solutions/netflix-logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 徐新华
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/global/init.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package global
10 |
11 | import (
12 | "flag"
13 | "fmt"
14 | "math/rand"
15 | "sync"
16 | "time"
17 |
18 | "github.com/spf13/viper"
19 | )
20 |
21 | var once = new(sync.Once)
22 |
23 | var (
24 | config = flag.String("config", "config", "配置文件名称,默认 config")
25 | )
26 |
27 | func Init() {
28 | once.Do(func() {
29 | if !flag.Parsed() {
30 | flag.Parse()
31 | }
32 |
33 | // 随机数种子
34 | rand.Seed(time.Now().UnixNano())
35 |
36 | // 配置文件名称
37 | viper.SetConfigName(*config)
38 | // 配置文件查找路径
39 | viper.AddConfigPath("/etc/golangclub/")
40 | viper.AddConfigPath("$HOME/.golangclub")
41 | viper.AddConfigPath(App.RootDir + "/config")
42 | // 读取配置文件
43 | err := viper.ReadInConfig()
44 | if err != nil {
45 | panic(fmt.Errorf("Fatal error config file: %s \n", err))
46 | }
47 |
48 | // 填充 global.App 需要的数据
49 | App.fillOtherField()
50 | })
51 | }
52 |
--------------------------------------------------------------------------------
/.air.conf:
--------------------------------------------------------------------------------
1 | # Config file for [Air](https://github.com/cosmtrek/air) in TOML format
2 |
3 | # Working directory
4 | # . or absolute path, please note that the directories following must be under root
5 | root = "."
6 | # Optional! If `watch_dir` is empty, use `root`.
7 | watch_dir = ""
8 | tmp_dir = "tmp"
9 |
10 | [build]
11 | # Just plain old shell command. You could use `make` as well.
12 | cmd = "gofmt -w . && go build github.com/polaris1119/golangclub"
13 | # Binary file yields from `cmd`.
14 | bin = "golangclub"
15 | # Customize binary.
16 | # full_bin = "APP_ENV=dev APP_USER=air ./tmp/studygolang"
17 | # This log file places in your tmp_dir.
18 | log = "air_errors.log"
19 | # Watch these filename extensions.
20 | include_ext = ["go"]
21 | # Ignore these filename extensions or directories.
22 | exclude_dir = ["assets", "tmp", "vendor", "template", "static", "docs", "node_modules"]
23 | # There's no necessary to trigger build each time file changes if it's too frequency.
24 | delay = 1000 # ms
25 |
26 | [log]
27 | # Show log time
28 | time = false
29 |
30 | [color]
31 | # Customize each part's color. If no color found, use the raw app log.
32 | main = "magenta"
33 | watcher = "cyan"
34 | build = "yellow"
35 | runner = "green"
36 |
37 | [misc]
38 | # Delete tmp directory on exit
39 | clean_on_exit = true
40 |
--------------------------------------------------------------------------------
/static/imgs/logos/cloudflare-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/go-logo-blue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/go-logo-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/logos/the-new-york-times-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/logos/mongodb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/logos/uber-app-icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | uber_rides_api_icon
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/static/imgs/solutions/CLI-green.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/static/imgs/solutions/webdev-green.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/http/controller/index.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package controller
10 |
11 | import (
12 | "github.com/labstack/echo/v4"
13 |
14 | . "github.com/polaris1119/golangclub/http"
15 | )
16 |
17 | type IndexController struct{}
18 |
19 | // RegisterRoutes 注册路由
20 | func (i IndexController) RegisterRoutes(e *echo.Echo) {
21 | e.GET("/", i.index)
22 | e.GET("/solutions", i.solution)
23 | e.GET("/learn", i.learn)
24 | e.GET("/gopher", i.gopher)
25 | e.GET("/about", i.about)
26 | e.GET("/solutions/:typ", i.solutionProxy)
27 | }
28 |
29 | // index 首页
30 | func (i IndexController) index(ctx echo.Context) error {
31 | return Render(ctx, "index.html", nil)
32 | }
33 |
34 | // solution 解决方案
35 | func (i IndexController) solution(ctx echo.Context) error {
36 | return Render(ctx, "solution.html", map[string]interface{}{"solution_active": "Header-menuItem--active"})
37 | }
38 |
39 | // learn 学习资源
40 | func (i IndexController) learn(ctx echo.Context) error {
41 | return Render(ctx, "learn.html", map[string]interface{}{"learn_active": "Header-menuItem--active"})
42 | }
43 |
44 | // gopher 名人
45 | func (i IndexController) gopher(ctx echo.Context) error {
46 | return Render(ctx, "gopher.html", map[string]interface{}{"gopher_active": "Header-menuItem--active"})
47 | }
48 |
49 | // about 关于
50 | func (i IndexController) about(ctx echo.Context) error {
51 | return Render(ctx, "about.html", map[string]interface{}{"about_active": "Header-menuItem--active"})
52 | }
53 |
54 | // solutionProxy 代理所有 solutions 子路由
55 | func (i IndexController) solutionProxy(ctx echo.Context) error {
56 | typ := ctx.Param("typ")
57 | return Render(ctx, "solutions/"+typ+".html", map[string]interface{}{"solution_active": "Header-menuItem--active"})
58 | }
59 |
--------------------------------------------------------------------------------
/static/js/base.js:
--------------------------------------------------------------------------------
1 | // Copyright 2019 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | /**
6 | * A bit of navigation related code for handling dismissible elements.
7 | */
8 | (function() {
9 | 'use strict';
10 |
11 | function registerHeaderListeners() {
12 | const header = document.querySelector('.js-header');
13 | const menuButtons = document.querySelectorAll('.js-headerMenuButton');
14 | menuButtons.forEach(button => {
15 | button.addEventListener('click', e => {
16 | e.preventDefault();
17 | header.classList.toggle('is-active');
18 | button.setAttribute(
19 | 'aria-expanded',
20 | header.classList.contains('is-active')
21 | );
22 | });
23 | });
24 |
25 | const scrim = document.querySelector('.js-scrim');
26 | scrim.addEventListener('click', e => {
27 | e.preventDefault();
28 | header.classList.remove('is-active');
29 | menuButtons.forEach(button => {
30 | button.setAttribute(
31 | 'aria-expanded',
32 | header.classList.contains('is-active')
33 | );
34 | });
35 | });
36 | }
37 |
38 | window.addEventListener('DOMContentLoaded', () => {
39 | registerHeaderListeners();
40 | });
41 |
42 | // Register feedback listeners.
43 | window.addEventListener('load', () => {
44 | const buttons = document.querySelectorAll('.js-feedbackButton');
45 | buttons.forEach(button => {
46 | button.addEventListener('click', sendFeedback);
47 | });
48 | });
49 |
50 | // Launches the feedback interface.
51 | function sendFeedback() {
52 | userfeedback.api.startFeedback({ productId: '5131929', bucket: 'Default' });
53 | }
54 |
55 | window.dataLayer = window.dataLayer || [];
56 | function gtag() {
57 | dataLayer.push(arguments);
58 | }
59 | gtag('js', new Date());
60 | gtag('config', 'UA-141356704-1');
61 | })();
62 |
--------------------------------------------------------------------------------
/http/http.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package http
10 |
11 | import (
12 | "bytes"
13 | "html/template"
14 | "net/http"
15 | "strings"
16 |
17 | "github.com/labstack/echo/v4"
18 |
19 | "github.com/polaris1119/golangclub/global"
20 | )
21 |
22 | const (
23 | LayoutTpl = "common/layout.html"
24 | )
25 |
26 | // Render html 输出
27 | func Render(ctx echo.Context, contentTpl string, data map[string]interface{}) error {
28 | if data == nil {
29 | data = map[string]interface{}{}
30 | }
31 |
32 | contentTpl = LayoutTpl + "," + contentTpl
33 | htmlFiles := strings.Split(contentTpl, ",")
34 | for i, contentTpl := range htmlFiles {
35 | htmlFiles[i] = global.App.TemplateDir + contentTpl
36 | }
37 | tpl, err := template.New("layout.html").Funcs(funcMap).
38 | Funcs(template.FuncMap{"include": tplInclude}).ParseFiles(htmlFiles...)
39 | if err != nil {
40 | return err
41 | }
42 |
43 | data["path"] = ctx.Path()
44 |
45 | return executeTpl(ctx, tpl, data)
46 | }
47 |
48 | func executeTpl(ctx echo.Context, tpl *template.Template, data map[string]interface{}) error {
49 | // css 和 js 可以每个页面保留一些自己特有的
50 |
51 | // 如果没有定义 css 和 js 模板,则定义之
52 | if jsTpl := tpl.Lookup("js"); jsTpl == nil {
53 | tpl.Parse(`{{define "js"}}{{end}}`)
54 | }
55 | if cssTpl := tpl.Lookup("css"); cssTpl == nil {
56 | tpl.Parse(`{{define "css"}}{{end}}`)
57 | }
58 |
59 | // 如果没有 seo 模板,则定义之
60 | if seoTpl := tpl.Lookup("seo"); seoTpl == nil {
61 | tpl.Parse(`{{define "seo"}}
62 |
63 |
64 | {{end}}`)
65 | }
66 |
67 | global.App.SetUptime()
68 | // global.App.SetCopyright()
69 |
70 | data["app"] = global.App
71 |
72 | // 记录处理时间
73 | // data["resp_time"] = time.Since(ctx.Get("req_start_time").(time.Time))
74 |
75 | buf := new(bytes.Buffer)
76 | err := tpl.Execute(buf, data)
77 | if err != nil {
78 | return err
79 | }
80 |
81 | return ctx.HTML(http.StatusOK, buf.String())
82 | }
83 |
--------------------------------------------------------------------------------
/static/imgs/logos/dropbox.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/solutions/target-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 |
8 |
12 |
14 |
16 |
18 |
22 |
26 |
29 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/global/app.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package global
10 |
11 | import (
12 | "fmt"
13 | "os"
14 | "path/filepath"
15 | "runtime"
16 | "strings"
17 | "sync"
18 | "time"
19 |
20 | "github.com/labstack/echo/v4"
21 | "github.com/spf13/viper"
22 |
23 | "github.com/polaris1119/golangclub/util"
24 | )
25 |
26 | func init() {
27 | App.Version = "V1.0"
28 | App.LaunchTime = time.Now()
29 |
30 | App.RootDir = "."
31 |
32 | if !viper.InConfig("http.port") {
33 | App.RootDir = inferRootDir()
34 | }
35 | App.TemplateDir = App.RootDir + "/template/"
36 |
37 | fileInfo, err := os.Stat(os.Args[0])
38 | if err != nil {
39 | panic(err)
40 | }
41 |
42 | App.Date = fileInfo.ModTime()
43 |
44 | App.Build.GoVersion = runtime.Version()
45 | App.Build.EchoVersion = echo.Version
46 | }
47 |
48 | // inferRootDir 递归推导项目根目录
49 | func inferRootDir() string {
50 | cwd, err := os.Getwd()
51 | if err != nil {
52 | panic(err)
53 | }
54 |
55 | var infer func(d string) string
56 | infer = func(d string) string {
57 | if d == "/" {
58 | panic("请确保在项目根目录或子目录下运行程序,当前在:" + cwd)
59 | }
60 |
61 | if util.Exist(d + "/config") {
62 | return d
63 | }
64 |
65 | return infer(filepath.Dir(d))
66 | }
67 |
68 | return infer(cwd)
69 | }
70 |
71 | var App = &app{}
72 |
73 | type app struct {
74 | Name string
75 | Version string
76 | Date time.Time
77 |
78 | // 项目根目录
79 | RootDir string
80 | // 模板根目录
81 | TemplateDir string
82 |
83 | // 启动时间
84 | LaunchTime time.Time
85 | Uptime time.Duration
86 |
87 | Domain string
88 | SEO map[string]string
89 |
90 | Build struct {
91 | GitCommitLog string
92 | BuildTime string
93 | GitRelease string
94 | GoVersion string
95 | EchoVersion string
96 | }
97 |
98 | locker sync.Mutex
99 | }
100 |
101 | func (a *app) SetUptime() {
102 | a.locker.Lock()
103 | defer a.locker.Unlock()
104 | a.Uptime = time.Now().Sub(a.LaunchTime)
105 | }
106 |
107 | func (a *app) FillBuildInfo(gitCommitLog, buildTime, gitRelease string) {
108 | a.Build.GitCommitLog = gitCommitLog
109 | a.Build.BuildTime = buildTime
110 |
111 | pos := strings.Index(gitRelease, "/")
112 | if pos >= -1 {
113 | a.Build.GitRelease = gitRelease[pos+1:]
114 | }
115 |
116 | fmt.Println(a)
117 | }
118 |
119 | func (a *app) fillOtherField() {
120 | a.Name = viper.GetString("name")
121 | a.Domain = viper.GetString("domain")
122 | a.SEO = viper.GetStringMapString("seo")
123 | }
124 |
125 | func (a *app) String() string {
126 | return "Build Info:" +
127 | "\nGit Commit Log: " + a.Build.GitCommitLog +
128 | "\nGit Release Info: " + a.Build.GitRelease +
129 | "\nBuild Time: " + a.Build.BuildTime +
130 | "\nGo Version: " + a.Build.GoVersion
131 | }
132 |
--------------------------------------------------------------------------------
/http/funcs.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2019. The StudyGolang Authors. All rights reserved.
3 | * Use of this source code is governed by a MIT-style
4 | * license that can be found in the LICENSE file.
5 | * https://golangclub.com
6 | * Author:polaris polaris@studygolang.com
7 | */
8 |
9 | package http
10 |
11 | import (
12 | "bytes"
13 | "encoding/json"
14 | "html/template"
15 | "math"
16 | "math/rand"
17 | "path/filepath"
18 | "strings"
19 | "time"
20 |
21 | "github.com/polaris1119/golangclub/global"
22 | )
23 |
24 | // funcMap 自定义模板函数
25 | var funcMap = template.FuncMap{
26 | // 转为前端显示需要的时间格式
27 | "formatTime": func(i interface{}) string {
28 | ctime, ok := i.(string)
29 | if !ok {
30 | return ""
31 | }
32 | t, _ := time.Parse("2006-01-02 15:04:05", ctime)
33 | return t.Format(time.RFC3339) + "+08:00"
34 | },
35 | "format": func(i interface{}, format string) string {
36 | switch i.(type) {
37 | case time.Time:
38 | return (i.(time.Time)).Format(format)
39 | case int64:
40 | val := i.(int64)
41 | return time.Unix(val, 0).Format(format)
42 | }
43 |
44 | return ""
45 | },
46 | "hasPrefix": func(s, prefix string) bool {
47 | if strings.HasPrefix(s, prefix) {
48 | return true
49 | }
50 | return false
51 | },
52 | "add": func(nums ...interface{}) int {
53 | total := 0
54 | for _, num := range nums {
55 | if n, ok := num.(int); ok {
56 | total += n
57 | }
58 | }
59 | return total
60 | },
61 | "mod": func(num1, num2 int) int {
62 | if num1 == 0 {
63 | num1 = rand.Intn(500)
64 | }
65 |
66 | return num1 % num2
67 | },
68 | "divide": func(num1, num2 int) int {
69 | return int(math.Ceil(float64(num1) / float64(num2)))
70 | },
71 | "explode": func(s, sep string) []string {
72 | return strings.Split(s, sep)
73 | },
74 | "noescape": func(s string) template.HTML {
75 | return template.HTML(s)
76 | },
77 | "timestamp": func(ts ...time.Time) int64 {
78 | if len(ts) > 0 {
79 | return ts[0].Unix()
80 | }
81 | return time.Now().Unix()
82 | },
83 | "parseJSON": func(str string) map[string]interface{} {
84 | result := make(map[string]interface{})
85 | json.Unmarshal([]byte(str), &result)
86 | return result
87 | },
88 | "genList": func(n int, steps ...int) []int {
89 | step := 1
90 | if len(steps) > 0 {
91 | step = steps[0]
92 | }
93 | num := int(math.Ceil(float64(n) / float64(step)))
94 | nums := make([]int, num)
95 | for i := 0; i < num; i++ {
96 | nums[i] = i + 1
97 | }
98 |
99 | return nums
100 | },
101 | }
102 |
103 | // tplInclude 支持 include 模板
104 | func tplInclude(file string, dot map[string]interface{}) template.HTML {
105 | var buffer = &bytes.Buffer{}
106 | tpl, err := template.New(filepath.Base(file)).Funcs(funcMap).ParseFiles(global.App.TemplateDir + file)
107 | if err != nil {
108 | return ""
109 | }
110 |
111 | err = tpl.Execute(buffer, dot)
112 | if err != nil {
113 | return ""
114 | }
115 |
116 | return template.HTML(buffer.String())
117 | }
118 |
--------------------------------------------------------------------------------
/template/about.html:
--------------------------------------------------------------------------------
1 | {{define "title"}}关于 — Go语言俱乐部{{end}}
2 | {{define "content"}}
3 |
4 | 关于
5 |
6 |
7 | Go 语言俱乐部 是 Go.dev 的中国本土化站点,由 Go 语言中文网 发起。 Go 语言中文网是 Go 语言爱好者分享 Go 语言知识,交流使用经验的学习家园,而 Go 语言俱乐部是 Go 语言爱好者的中心,提供来自整个 Go 生态系统的集中和策划的资源。
8 |
9 |
10 | Go 语言俱乐部提供:
11 |
12 |
13 | 集中展示发布在 index.golang.org 的 Go 包和模块信息
14 | 基本学习资源
15 | 关键用例和案例研究
16 |
17 |
18 | Go 语言俱乐部目前处于 MVP 状态。 我们为我们所建立的东西感到骄傲,并且很高兴与社区分享它。我们希望你能在使用 Go 语言俱乐部中找到价值和乐趣。开发版只有一小部分功能,我们正在积极的寻求反馈。如果您有任何想法、建议或问题,请联系我们。
19 |
20 | Sharing feedback / Reporting an issue
21 |
22 | On the footer of every page there are two links, “Share Feedback” and “Report an issue”. These links will enable you to capture a screenshot of the page you are on, annotate that screenshot, and then send this directly to the go.dev team.
23 |
24 | Or you can send your bugs, ideas, feature requests and questions to go-discovery-feedback@google.com .
25 |
26 | Adding a package
27 |
28 | To add a package or module, simply fetch it from proxy.golang.org. Documentation is generated based on Go source code downloaded from the proxy.golang.org/<module>@<version>.zip. New module versions are fetched from index.golang.org and added to the go.dev site every few minutes.
29 |
30 | The guidelines for writing documentation for the godoc tool apply to go.dev.
31 |
32 | It’s important to write a good summary of the package in the first sentence of the package comment. The go.dev site indexes the first sentence and displays it in search results.
33 |
34 | Removing a package
35 |
36 | If you would like a package removed, please send an email to go-discovery-feedback@google.com , with the import path or module path that you want to remove.
37 |
38 | License policy
39 |
40 | Information for a given package or module may be limited if we are not able to detect a suitable license. See our license policy for more information.
41 |
42 |
43 | {{end}}
--------------------------------------------------------------------------------
/static/imgs/logos/github.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/static/imgs/logos/uber.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
14 |
25 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/static/imgs/solutions/uber-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
14 |
25 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/static/imgs/logos/kubernetes.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/solutions/dropbox-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
11 |
13 |
27 |
28 |
29 |
30 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/static/imgs/logos/twitch.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
44 |
46 |
47 |
49 | image/svg+xml
50 |
52 |
53 |
54 |
55 |
56 |
61 |
65 |
70 |
71 |
75 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/static/imgs/solutions/twitch-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
44 |
46 |
47 |
49 | image/svg+xml
50 |
52 |
53 |
54 |
55 |
56 |
61 |
65 |
70 |
71 |
75 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/static/imgs/logos/capital-one.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/logos/stripe.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
19 |
21 |
44 |
46 |
47 |
49 | image/svg+xml
50 |
52 |
53 |
54 |
55 |
56 |
61 |
64 |
70 |
71 |
77 |
78 |
83 |
84 |
92 |
93 |
99 |
100 |
106 |
107 |
113 |
114 |
115 |
116 |
121 |
122 |
--------------------------------------------------------------------------------
/static/imgs/logos/google-cloud.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/solutions/capital-one-logo.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
3 |
--------------------------------------------------------------------------------
/static/imgs/logos/didi.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 新logo
5 | Created with Sketch.
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/static/imgs/logos/comcast.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
16 |
19 |
22 |
25 |
27 |
29 |
31 |
35 |
42 |
47 |
52 |
58 |
62 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/static/imgs/solutions/ops-green.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/static/imgs/pink.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/gophers/peach.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/static/imgs/solutions/cloud-green.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/static/imgs/gophers/happy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/template/common/layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{template "title" .}}
5 |
6 | {{template "seo" .}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
57 |
88 |
89 |
90 |
91 |
92 | {{template "content" .}}
93 |
94 |
95 |
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------
/static/imgs/logos/mercadoLibre.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
14 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
38 |
44 |
49 |
52 |
57 |
60 |
61 |
62 |
63 |
65 |
67 |
68 |
71 |
82 |
87 |
88 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/static/imgs/solutions/mercadolibre-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
14 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
33 |
38 |
44 |
49 |
52 |
57 |
60 |
61 |
62 |
63 |
65 |
67 |
68 |
71 |
82 |
87 |
88 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/static/imgs/pilot-bust.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/template/solutions/webdev.html:
--------------------------------------------------------------------------------
1 | {{define "title"}}Go Web 开发 - golangclub.com{{end}}
2 | {{define "content"}}
3 |
4 |
5 | Go Web 开发
6 |
8 |
9 | Go 旨在使开发人员能够快速开发可扩展的、安全的 Web 应用程序。Go 标准库看自带易用的、安全的、高性能的、稳健的 Web 服务器,以及 Web 模板库。Go 完美支持了所有的新技术, 比如:HTTP/2 ,各种流行的数据库,包括MySQL 、
12 | MongoDB 、
13 | ElasticSearch ,最新的加密技术,如 TLS 1.3 等等。由于 Go 具有极好的可移植性,所以 Go Web 应用程序可以运行在任何的环境、云平台和操作系统上
16 |
17 |
18 | 对于企业来说,Go 是快速跨平台部署的首选。凭借其 Goroutine、本地编译以及基于 URI 的 Package 命名空间,使 Go 代码能够快速编译为单个的、非常小的二进制文件(具有零依赖性)
19 |
20 | 来自 QArea 市场经理 Andrew
22 | Smith 写道:“如果你正在为网络编程,移动开发,微服务,ERP 系统寻找强力的工具,根据很多真实的案例证明,进行相同类型的任务开发,使用 Go 进行 Web 开发比 Python 要快很多”
23 |
24 |
25 |
28 |
29 | Go 语言是我见过的和使用过的最简单的语言,对我来说,Go 比 JavaScript 更易于学习
30 |
31 |
34 |
35 |
36 | Bayburtsyan 总结了他的公司改用 Go 的五个关键原因:
37 |
38 |
39 | 编译成单个二进制文件 — “根据操作系统类型和体系结构的不同,Go 会使用静态链接将所有依赖项库和模块组合到一个二进制文件中”
40 |
41 |
42 |
43 | 静态类型 — “对大规模应用来说,类型系统真的很重要”
44 |
45 |
46 |
47 |
48 | 性能 — “Go 性能这么好的原因是由于其并发模型以及CPU可扩展性。当我们需要处理很多内部请求时,相比于 Python 的线程,Goroutines 在资源使用上会少 10 倍以上”
49 |
50 |
51 |
52 | 不再需要 Web 框架 — “实际上在大多数情况下,我们不再需要任何第三方库”
53 |
54 |
55 |
56 | 强大的 IDE 支持和调试 — “在我们的项目用 Go 重写后,我们的代码量比以前减少了 64% ”
57 |
58 |
59 |
60 | Go 明星用户和项目
61 |
62 |
126 |
127 | 核心解决方案
128 |
129 | Go web 开发书单
130 |
131 |
200 |
201 |
205 |
206 |
207 | Gin 用 Go 编写的一个 Web 应用框架,对比其它主流的同类框架,他有更好的性能,更快的路由,超大规模的使用人群,丰富的中间件。相信我你会爱上 Gin,不解释
208 |
209 | Echo 另外一个高性能,被大规模使用的 Go 框架。具有性能优秀的 Router,支持高扩展,对 XML、JSON 的完美解析,支持 HTTP/2 等等
210 |
211 | Beego 国产的明星级别的 Go 框架,是一个优秀的 MVC 框架 ,主要设计灵感来源于 Tornado、Sinatra 和 Flask 这三个框架
212 |
213 | Macaron 一款具有高生产力和模块化设计的 Go Web 框架。框架秉承了 Martini 的基本思想,并在此基础上做出高级扩展
214 |
215 | Buffalo 一款能够进行快速 Go Web 开发的全栈式框架,由 Go 和 Javascript 共同组成了一个强大的生态
216 |
217 | Revel 一款高生产力的、全栈的 Go Web 开发框架
218 |
219 |
220 | Faygo 是一款快速、简洁的 Go Web 框架,可用极少的代码开发出高性能的 Web 应用程序(尤其是 API 接口)
221 |
222 | Gorilla ,一个 Go Web 编程工具包。这个工具包提供 Router、Dispatcher、Context、 RPC、Schema、Sessions、Webscoket等等
223 |
224 |
225 |
229 |
230 |
241 |
242 |
246 |
247 |
252 |
253 |
257 |
258 |
273 |
274 |
278 |
279 |
288 |
289 | 课程
290 |
291 |
295 |
296 | 项目
297 |
298 |
299 |
300 | gopherjs 一个编译器,将 Gopher 用 Go 编写代码轻松转换成 JS 前端代码,并且能够兼容所有的浏览器
301 | Hugo 世界上最快构建网站的框架
302 | Mattermost 一个灵活的、开源的、安全的团队协作平台
303 | Caddy Go 语言编写的,一款功能强大的、企业级的开源 Web 服务器,并自带 Automatic HTTPS
304 |
305 |
306 |
307 |
308 |
309 | {{end}}
--------------------------------------------------------------------------------
/template/solution.html:
--------------------------------------------------------------------------------
1 | {{define "title"}}解决方案 — Go语言俱乐部{{end}}
2 | {{define "content"}}
3 |
8 |
9 |
10 |
11 | 利用 Go 来改进你的开发过程
12 |
13 |
14 |
15 |
93 |
273 | {{end}}
274 |
--------------------------------------------------------------------------------