hopefully the the next-generation backend server of bgm.tv

基于 python 的新 api server

开发环境

python 版本: 3.8

依赖管理: poetry

web 框架: fastapi

quick start:

git clone https://github.com/bangumi/server bangumi-server
cd bangumi-server
python -m venv .venv # MUST use python 3.8
source .venv/bin/activate
poetry install --remove-untracked
pre-commit install

设置

可设置的环境变量

  • MYSQL_HOST 默认 127.0.0.1
  • MYSQL_PORT 默认 3306
  • MYSQL_DB 默认 bangumi
  • MYSQL_USER 无默认值
  • MYSQL_PASS 无默认值

启动服务器

uvicorn pol.server:app --reload --port 3000

后端环境

https://github.com/bangumi/dev-env

测试(需要数据库)

pytest

代码风格

以 LF 为换行符

python

启用 pre-commit

pre-commit insall

pre-commit 会在当前仓库安装一个 git hook,在每次 commit 前自动运行。

也可以手动运行

pre-commit run #only check changed files
pre-commit run --all-files # check all files

lint: flake8

配置文件

非 python 文件(yaml, json, markdown 等)使用 prettier 进行格式化。

Comments
  • feat: timeline

    feat: timeline

    timeline is a post arrangement component in bgm, which based on posts' chrono order

    this commit introduces the package timeline and wraps its CRUD functionality into interface

    #176

  • feat: topics and comments

    feat: topics and comments

    获取条目全部讨论

    GET /p/subjects/{subject_id}/topics

    Query

    • limit
    • offset

    响应:

    {
      "data": [
        {
          "created_at": "2008-07-14T15:34:07+08:00",
          "creator": {
            "username": "2",
            "nickname": "nickname 2"
          },
          "title": "拿这个来测试",
          "id": 1,
          "replies": 76
        }
      ],
      "hasMore": false,
      "limit": 10,
      "offset": 0
    }
    
    获取条目讨论内容

    GET /p/subjects/{subject_id}/topics/{topic_id}

    Query

    • limit
    • offset

    响应:

    {
      "created_at": "2008-07-14T15:34:07+08:00",
      "comments": {
        "data": [
          {
            "created_at": "2016-01-15T20:58:20+08:00",
            "creator": {
              "username": "",
              "nickname": ""
            },
            "text": "这个帖子竟然没人挖(bgm38)",
            "replies": [
              {
                "created_at": "2016-01-17T01:25:12+08:00",
                "creator": {
                  "username": "",
                  "nickname": ""
                },
                "text": "专业挖坟的么=_=",
                "id": 68013
              }
            ],
            "id": 67970
          }
        ],
        "has_more": true,
        "limit": 10,
        "offset": 0
      },
      "creator": {
        "username": "2",
        "nickname": "nickname 2"
      },
      "title": "拿这个来测试",
      "id": 1,
      "replies": 76
    }
    
    获取角色讨论内容

    GET /p/characters/{character_id}/comments

    Query

    • limit
    • offset

    响应:

    {
      "data": [
        {
          "created_at": "2012-11-08T22:36:32+08:00",
          "creator": {
            "username": "",
            "nickname": ""
          },
          "text": "9/101\n渣渣~",
          "replies": [
            {
              "created_at": "2012-11-08T22:40:32+08:00",
              "creator": {
                "username": "14127",
                "nickname": "nickname 14127"
              },
              "text": "哈哈 我也是9/101",
              "id": 1039
            }
      ],
      "has_more": false,
      "limit": 30,
      "offset": 0
    }
    

    close #67

  • 目录直接相关的 API

    目录直接相关的 API

    v0.Post("/indices", req.JSON, h.NeedLogin, h.NewIndex)
    v0.Put("/indices/:id", req.JSON, h.NeedLogin, h.UpdateIndex)
    // v0.Delete("/indices/:id", h.NeedLogin, h.DeleteIndex) 该 PR 不实现该方法,相关代码已删除
    
    v0.Post("/indices/:id/subjects", req.JSON, h.NeedLogin, h.AddIndexSubject)
    v0.Put("/indices/:id/subjects/:subject_id", req.JSON, h.NeedLogin, h.UpdateIndexSubject)
    v0.Delete("/indices/:id/subjects/:subject_id", h.NeedLogin, h.RemoveIndexSubject) 
    
  • feat(pm & notification): pm related apis and a notification counting api

    feat(pm & notification): pm related apis and a notification counting api

    1. /pms/list(GET):
      1. ?folder=inbox(收件箱)
      2. ?folder=outbox (已发送) example response
      {
        "data": [
          {
            "created_at": "0001-01-01T00:00:00Z",
            "related_message": {
              "created_at": "0001-01-01T00:00:00Z",
              "title": "",
              "content": "",
              "id": 1,
              "new": false
            },
            "sender": {
              "avatar": {
                "large": "",
                "medium": "",
                "small": ""
              },
              "sign": "",
              "url": "",
              "username": "",
              "nickname": "",
              "id": 1,
              "user_group": 1
            },
            "receiver": {
              "avatar": {
                "large": "",
                "medium": "",
                "small": ""
              },
              "sign": "",
              "url": "",
              "username": "",
              "nickname": "",
              "id": 2,
              "user_group": 1
            },
            "title": "",
            "content": "",
            "id": 2,
            "new": false
          }
        ]
      }
      
    2. /pms/related-msgs/:id(GET)

    example response

    [
      {
        "created_at": "0001-01-01T00:00:00Z",
        "sender": {
          "avatar": {
            "large": "",
            "medium": "",
            "small": ""
          },
          "sign": "",
          "url": "",
          "username": "",
          "nickname": "",
          "id": 1,
          "user_group": 1
        },
        "receiver": {
          "avatar": {
            "large": "",
            "medium": "",
            "small": ""
          },
          "sign": "",
          "url": "",
          "username": "",
          "nickname": "",
          "id": 2,
          "user_group": 1
        },
        "title": "",
        "content": "",
        "id": 1,
        "new": false
      }
    ]
    
    1. /pms/counts(GET)

    example response

    {
      "unread": 0,
      "inbox": 0,
      "outbox": 0
    }
    
    1. /pms/contacts/recent(GET)(最新的15个收件人)

    example response

    [
      {
        "avatar": {
          "large": "",
          "medium": "",
          "small": ""
        },
        "sign": "",
        "url": "",
        "username": "",
        "nickname": "",
        "id": 1,
        "user_group": 1
      }
    ]
    
    1. /pms/read(PATCH)(标记已读) example request body
    {
      "id": 1
    }
    

    成功返回204

    1. /pms(POST) example request body
    {
      "title": "",
      "content": "",
      "related_id": 1, // or empty
      "receiver_ids": [1],
      "sender_id": 1
    }
    

    example response

    [
      {
        "created_at": "0001-01-01T00:00:00Z",
        "sender": {
          "avatar": {
            "large": "",
            "medium": "",
            "small": ""
          },
          "sign": "",
          "url": "",
          "username": "",
          "nickname": "",
          "id": 1,
          "user_group": 1
        },
        "receiver": {
          "avatar": {
            "large": "",
            "medium": "",
            "small": ""
          },
          "sign": "",
          "url": "",
          "username": "",
          "nickname": "",
          "id": 2,
          "user_group": 1
        },
        "title": "",
        "content": "",
        "id": 1,
        "new": false
      }
    ]
    
    1. /pms(DELETE) example request body
    {
      "id": 1
    }
    

    成功返回204

    1. /notifications/count(GET) example response
    0
    
  • feat: redirect to image by id and type

    feat: redirect to image by id and type

    重定向至条目图片

    GET /v0/subjects/{subject_id}/image

    Request Query:

    • type: {small|grid|large|medium|common}

    Response: 302 image_url

    重定向至角色图片

    GET /v0/characters/{character_id}/image

    Request Query:

    • type: {small|grid|large|medium}

    Response: 302 image_url

    重定向至人物图片

    GET /v0/persons/{person_id}/image

    Request Query:

    • type: {small|grid|large|medium}

    Response: 302 image_url

    close #31

  • WIP: Indices

    WIP: Indices

    讨论 Issue #230

    [POST] /indices 新建目录 请求数据

    {
        "title": "标题信息",
        "description": "目录介绍"
    }
    

    返回数据

    {
        "created_at": "2022-10-06T13:10:25+08:00",
        "update_at": "2022-10-06T13:10:25+08:00",
        "creator": {
            "username": "382951",
            "nickname": "树洞酱"
        },
        "title": "本地测试",
        "description": "这里是目录的介绍,12345123123",
        "total": 0,
        "id": 15471,
        "stat": {
            "comments": 0,
            "collects": 0
        },
        "ban": false,
        "nsfw": false
    }
    

    [PUT] /indices/<index_id> 修改目录 请求数据

    {
        "title": "标题信息",
        "description": "目录介绍"
    }
    

    返回数据 , 200 代表修改成功

    [DELETE] /indices/<index_id> 删除目录 请求数据 返回数据 , 200 代表修改成功

    [POST] /indices/<index_id>/subjects 在目录中增加条目 请求数据

    {
        "subject_id": 5,
        "sort": 13,
        "comment": "测试 comment"
    }
    

    返回数据

    {
        "added_at": "2022-10-07T12:28:08+08:00",
        "date": null,
        "images": {
            "small": "https://lain.bgm.tv/pic/cover/s/e4/da/5_wUARf.jpg",
            "grid": "https://lain.bgm.tv/pic/cover/g/e4/da/5_wUARf.jpg",
            "large": "https://lain.bgm.tv/pic/cover/l/e4/da/5_wUARf.jpg",
            "medium": "https://lain.bgm.tv/pic/cover/m/e4/da/5_wUARf.jpg",
            "common": "https://lain.bgm.tv/pic/cover/c/e4/da/5_wUARf.jpg"
        },
        "name": "Call of Duty 4",
        "name_cn": "使命召唤 4: 现代战争",
        "comment": "测试 comment",
        "infobox": [],
        "id": 5,
        "type": 4
    }
    

    [PUT] /indices/<index_id>/subjects/<subject_id> 在目录中修改条目 请求数据

    {
        "sort": 13,
        "comment": "测试 comment"
    }
    

    返回数据 , 200 代表修改成功

    [DELETE] /indices/<index_id>/subjects/<subject_id> 在目录中删除条目 请求数据 返回数据 , 200 代表修改成功

    [GET] /users//indices 获取用户创建的条目 请求数据 返回数据

    [
        {
            "created_at": "2022-10-06T11:31:42+08:00",
            "update_at": "1970-01-01T08:00:00+08:00",
            "creator": {
                "username": "382951",
                "nickname": "树洞酱"
            },
            "title": "本地测试2",
            "description": "123",
            "total": 0,
            "id": 15467,
            "stat": {
                "comments": 0,
                "collects": 0
            },
            "ban": false,
            "nsfw": false
        }
    ]
    

    [GET] /users//indices/collect 获取用户收藏的条目 请求数据 返回数据

    {
        "user": {
            "username": "382951",
            "nickname": "树洞酱"
        },
        "indices": [
            {
                "created_at": "2022-10-06T11:31:42+08:00",
                "update_at": "1970-01-01T08:00:00+08:00",
                "creator": {
                    "username": "382951",
                    "nickname": "树洞酱"
                },
                "title": "本地测试2",
                "description": "123",
                "total": 0,
                "id": 15467,
                "stat": {
                    "comments": 0,
                    "collects": 0
                },
                "ban": false,
                "nsfw": false
            }
        ]
    }
    

    [POST] /indices/<index_id>/collect 收藏条目 请求数据 返回数据 , 200 代表修改成功,重复收藏会返回 202 (Accpeted)

    [DELETE] /indices/<index_id>/collect 取消收藏条目 请求数据 返回数据 , 200 代表修改成功, 404 代表用户没有收藏该条目

  • 应用崩溃

    应用崩溃

    今天看错误日志看到有服务重启,检查了下log,没明白是为啥发生的。

    对应的是这个 commit https://github.com/bangumi/server/tree/v0.17.1

    runtime: marked free object in span 0x7fbb8e183db8, elemsize=2048 freeindex=1 (bad use of unsafe.Pointer? try -d=checkptr)
    0xc000724000 alloc marked  
    0xc000724800 free  unmarked
    0xc000725000 free  marked   zombie
    0x000000c000725000:  0x0000000000000000  0x0000000000000000 
    0x000000c000725010:  0x0000000000000000  0x0000000000000000 
    0x000000c000725020:  0x0000000000000000  0x0000000000000000 
    0x000000c000725030:  0x0000000000000000  0x0000000000000000 
    0x000000c000725040:  0x0000000000000000  0x0000000000000000 
    0x000000c000725050:  0x0000000000000000  0x0000000000000000 
    0x000000c000725060:  0x0000000000000000  0x0000000000000000 
    0x000000c000725070:  0x0000000000000000  0x0000000000000000 
    0x000000c000725080:  0x0000000000000000  0x0000000000000000 
    0x000000c000725090:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250a0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250b0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250c0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250d0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250e0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007250f0:  0x0000000000000000  0x0000000000000000 
    0x000000c000725100:  0x0000000000000000  0x0000000000000000 
    0x000000c000725110:  0x0000000000000000  0x0000000000000000 
    0x000000c000725120:  0x0000000000000000  0x0000000000000000 
    0x000000c000725130:  0x0000000000000000  0x0000000000000000 
    0x000000c000725140:  0x0000000000000000  0x0000000000000000 
    0x000000c000725150:  0x0000000000000000  0x0000000000000000 
    0x000000c000725160:  0x0000000000000000  0x0000000000000000 
    0x000000c000725170:  0x0000000000000000  0x0000000000000000 
    0x000000c000725180:  0x0000000000000000  0x0000000000000000 
    0x000000c000725190:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251a0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251b0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251c0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251d0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251e0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007251f0:  0x0000000000000000  0x0000000000000000 
    0x000000c000725200:  0x0000000000000000  0x0000000000000000 
    0x000000c000725210:  0x0000000000000000  0x0000000000000000 
    0x000000c000725220:  0x0000000000000000  0x0000000000000000 
    0x000000c000725230:  0x0000000000000000  0x0000000000000000 
    0x000000c000725240:  0x0000000000000000  0x0000000000000000 
    0x000000c000725250:  0x0000000000000000  0x0000000000000000 
    0x000000c000725260:  0x0000000000000000  0x0000000000000000 
    0x000000c000725270:  0x0000000000000000  0x0000000000000000 
    0x000000c000725280:  0x0000000000000000  0x0000000000000000 
    0x000000c000725290:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252a0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252b0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252c0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252d0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252e0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007252f0:  0x0000000000000000  0x0000000000000000 
    0x000000c000725300:  0x0000000000000000  0x0000000000000000 
    0x000000c000725310:  0x0000000000000000  0x0000000000000000 
    0x000000c000725320:  0x0000000000000000  0x0000000000000000 
    0x000000c000725330:  0x0000000000000000  0x0000000000000000 
    0x000000c000725340:  0x0000000000000000  0x0000000000000000 
    0x000000c000725350:  0x0000000000000000  0x0000000000000000 
    0x000000c000725360:  0x0000000000000000  0x0000000000000000 
    0x000000c000725370:  0x0000000000000000  0x0000000000000000 
    0x000000c000725380:  0x0000000000000000  0x0000000000000000 
    0x000000c000725390:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253a0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253b0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253c0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253d0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253e0:  0x0000000000000000  0x0000000000000000 
    0x000000c0007253f0:  0x0000000000000000  0x0000000000000000 
    0xc000725800 free  unmarked
    fatal error: found pointer to free object
    runtime stack:
    runtime.throw({0x113fa1f, 0xc000725400})
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/panic.go:1198 +0x71
    runtime.(*mspan).reportZombies(0x7fbb8e183db8)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgcsweep.go:691 +0x2e5
    runtime.(*sweepLocked).sweep(0x0, 0x0)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgcsweep.go:519 +0x33a
    runtime.(*mcentral).uncacheSpan(0xc000061e60, 0xc000061e50)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mcentral.go:223 +0xa9
    runtime.(*mcache).releaseAll(0x7fbbb4f59108)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mcache.go:279 +0x125
    runtime.(*mcache).prepareForSweep(0x7fbbb4f59108)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mcache.go:317 +0x39
    runtime.procresize(0x2)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/proc.go:5060 +0x476
    runtime.startTheWorldWithSema(0x1)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/proc.go:1256 +0x8b
    runtime.gcMarkTermination.func3()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgc.go:1032 +0x1e
    runtime.systemstack()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/asm_amd64.s:383 +0x49
    goroutine 5 [running]:
    runtime.systemstack_switch()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/asm_amd64.s:350 fp=0xc000048560 sp=0xc000048558 pc=0x464a60
    runtime.gcMarkTermination(0x1c38fa0)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgc.go:1032 +0x4fa fp=0xc000048708 sp=0xc000048560 pc=0x41b93a
    runtime.gcMarkDone()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgc.go:902 +0x27d fp=0xc000048760 sp=0xc000048708 pc=0x41b33d
    runtime.gcBgMarkWorker()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgc.go:1330 +0x2f1 fp=0xc0000487e0 sp=0xc000048760 pc=0x41c4d1
    runtime.goexit()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/asm_amd64.s:1581 +0x1 fp=0xc0000487e8 sp=0xc0000487e0 pc=0x466b41
    created by runtime.gcBgMarkStartWorkers
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/mgc.go:1124 +0x25
    goroutine 1 [chan receive, 359 minutes]:
    go.uber.org/fx.(*App).run(0xc00007f380, 0x0)
    	/home/runner/go/pkg/mod/go.uber.org/[email protected]/app.go:618 +0x91
    go.uber.org/fx.(*App).Run(0xc00007f380)
    	/home/runner/go/pkg/mod/go.uber.org/[email protected]/app.go:605 +0x2b
    main.start()
    	/home/runner/work/server/server/main.go:89 +0x615
    main.main()
    	/home/runner/work/server/server/main.go:48 +0x2b
    goroutine 22 [select, 2 minutes]:
    github.com/uber-go/tally/v4.(*scope).reportLoop(0xc000414900, 0x0)
    	/home/runner/go/pkg/mod/github.com/uber-go/tally/[email protected]/scope.go:238 +0xd6
    github.com/uber-go/tally/v4.newRootScope.func1()
    	/home/runner/go/pkg/mod/github.com/uber-go/tally/[email protected]/scope.go:180 +0x65
    created by github.com/uber-go/tally/v4.newRootScope
    	/home/runner/go/pkg/mod/github.com/uber-go/tally/[email protected]/scope.go:178 +0x78d
    goroutine 25 [select, 359 minutes]:
    database/sql.(*DB).connectionOpener(0xc0000a4c30, {0x139ded0, 0xc00065c140})
    	/opt/hostedtoolcache/go/1.17.10/x64/src/database/sql/sql.go:1196 +0x93
    created by database/sql.OpenDB
    	/opt/hostedtoolcache/go/1.17.10/x64/src/database/sql/sql.go:794 +0x188
    goroutine 26 [select, 2 minutes]:
    github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1()
    	/home/runner/go/pkg/mod/github.com/go-sql-driver/[email protected]/connection.go:614 +0xb0
    created by github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher
    	/home/runner/go/pkg/mod/github.com/go-sql-driver/[email protected]/connection.go:611 +0x105
    goroutine 27 [select, 359 minutes]:
    database/sql.(*DB).connectionCleaner(0xc0000a4c30, 0x0)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/database/sql/sql.go:1068 +0xbd
    created by database/sql.(*DB).startCleanerLocked
    	/opt/hostedtoolcache/go/1.17.10/x64/src/database/sql/sql.go:1055 +0x105
    goroutine 30 [select, 2 minutes]:
    github.com/go-redis/redis/v8/internal/pool.(*ConnPool).reaper(0xc0005c7860, 0xc0000b9860)
    	/home/runner/go/pkg/mod/github.com/go-redis/redis/[email protected]/internal/pool/pool.go:485 +0xda
    created by github.com/go-redis/redis/v8/internal/pool.NewConnPool
    	/home/runner/go/pkg/mod/github.com/go-redis/redis/[email protected]/internal/pool/pool.go:111 +0x229
    goroutine 35 [IO wait, 2 minutes]:
    internal/poll.runtime_pollWait(0x7fbb8e2f6278, 0x72)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/netpoll.go:303 +0x85
    internal/poll.(*pollDesc).wait(0xc000684000, 0xc0006e9000, 0x0)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
    internal/poll.(*pollDesc).waitRead(...)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_poll_runtime.go:89
    internal/poll.(*FD).Read(0xc000684000, {0xc0006e9000, 0x1000, 0x1000})
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_unix.go:167 +0x25a
    net.(*netFD).Read(0xc000684000, {0xc0006e9000, 0xc000684000, 0xc000778000})
    	/opt/hostedtoolcache/go/1.17.10/x64/src/net/fd_posix.go:56 +0x29
    net.(*conn).Read(0xc0006e6008, {0xc0006e9000, 0xfd7000, 0xc0006f9350})
    	/opt/hostedtoolcache/go/1.17.10/x64/src/net/net.go:183 +0x45
    bufio.(*Reader).fill(0xc0006fe300)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/bufio/bufio.go:101 +0x103
    bufio.(*Reader).Peek(0xc0006fe300, 0x1)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/bufio/bufio.go:139 +0x5d
    github.com/valyala/fasthttp.(*Server).serveConn(0xc00065ab40, {0x13b9990, 0xc0006e6008})
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/server.go:2155 +0x5d1
    github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0007fe000, 0xc0006ec000)
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:224 +0xa9
    github.com/valyala/fasthttp.(*workerPool).getCh.func1()
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:196 +0x38
    created by github.com/valyala/fasthttp.(*workerPool).getCh
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:195 +0x1b5
    goroutine 8 [syscall, 359 minutes]:
    os/signal.signal_recv()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/sigqueue.go:169 +0x98
    os/signal.loop()
    	/opt/hostedtoolcache/go/1.17.10/x64/src/os/signal/signal_unix.go:24 +0x19
    created by os/signal.Notify.func1.1
    	/opt/hostedtoolcache/go/1.17.10/x64/src/os/signal/signal.go:151 +0x2c
    goroutine 11 [sleep, 2 minutes]:
    time.Sleep(0x2540be400)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/time.go:193 +0x12e
    github.com/valyala/fasthttp.(*workerPool).Start.func2()
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:67 +0x56
    created by github.com/valyala/fasthttp.(*workerPool).Start
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:59 +0xde
    goroutine 10 [IO wait, 2 minutes]:
    internal/poll.runtime_pollWait(0x7fbb8e2f6368, 0x72)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/netpoll.go:303 +0x85
    internal/poll.(*pollDesc).wait(0xc0007bad00, 0xc000040800, 0x0)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_poll_runtime.go:84 +0x32
    internal/poll.(*pollDesc).waitRead(...)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_poll_runtime.go:89
    internal/poll.(*FD).Accept(0xc0007bad00)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/internal/poll/fd_unix.go:402 +0x22c
    net.(*netFD).accept(0xc0007bad00)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/net/fd_unix.go:173 +0x35
    net.(*TCPListener).accept(0xc0003e45b8)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/net/tcpsock_posix.go:140 +0x28
    net.(*TCPListener).Accept(0xc0003e45b8)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/net/tcpsock.go:262 +0x3d
    github.com/valyala/fasthttp.acceptConn(0xc00065ab40, {0x1395818, 0xc0003e45b8}, 0xc000804e80)
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/server.go:1913 +0x62
    github.com/valyala/fasthttp.(*Server).Serve(0xc00065ab40, {0x1395818, 0xc0003e45b8})
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/server.go:1826 +0x4ea
    github.com/gofiber/fiber/v2.(*App).Listener(0xc00000a780, {0x1395818, 0xc0003e45b8})
    	/home/runner/go/pkg/mod/github.com/gofiber/fiber/[email protected]/app.go:776 +0xd0
    github.com/bangumi/server/internal/web.Listen.func1.1()
    	/home/runner/work/server/server/internal/web/index.go:88 +0x37
    created by github.com/bangumi/server/internal/web.Listen.func1
    	/home/runner/work/server/server/internal/web/index.go:87 +0x1da
    goroutine 39 [sleep]:
    time.Sleep(0x3b9aca00)
    	/opt/hostedtoolcache/go/1.17.10/x64/src/runtime/time.go:193 +0x12e
    github.com/valyala/fasthttp.updateServerDate.func1()
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/header.go:2050 +0x1e
    created by github.com/valyala/fasthttp.updateServerDate
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/header.go:2048 +0x27
    goroutine 22000 [select, 2 minutes]:
    github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher.func1()
    	/home/runner/go/pkg/mod/github.com/go-sql-driver/[email protected]/connection.go:614 +0xb0
    created by github.com/go-sql-driver/mysql.(*mysqlConn).startWatcher
    	/home/runner/go/pkg/mod/github.com/go-sql-driver/[email protected]/connection.go:611 +0x105
    goroutine 32742 [chan receive, 2 minutes]:
    github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc0007fe000, 0xc0003380c0)
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:219 +0x65
    github.com/valyala/fasthttp.(*workerPool).getCh.func1()
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:196 +0x38
    created by github.com/valyala/fasthttp.(*workerPool).getCh
    	/home/runner/go/pkg/mod/github.com/valyala/[email protected]/workerpool.go:195 +0x1b5
    
    
  • 获取条目讨论

    获取条目讨论

    https://github.com/bangumi/server/issues/53

    Endpoint

    • [ ] GET /v0/subjects/:subjectId/topics 获取条目下全部讨论
    • [ ] GET /v0/subjects-topics/:topicId 获取条目讨论内容

    Authz

    • [ ] 讨论查看权限
    • [ ] 回复查看权限
  • feat(api): subject api

    feat(api): subject api

    条目

    GET /v0/subjects/{subject_id}

    {
      "id": 8,
      "type": 2,
      "name": "コードギアス 反逆のルルーシュR2",
      "name_cn": "Code Geass 反叛的鲁路修R2",
      "summary": "  “东京决战”一年后,布里塔尼亚少年鲁路修在11区(原日本国)过着平凡的学生生活。但是,鲁路修与弟弟罗洛的一次出行,遇到了黑色骑士团的余党。在与少女C.C再次结成契约之后,尘封的记忆摆在了鲁路修的面前。\r\n  身为帝国王子的鲁路修,为了建立人人平等的世界、让妹妹娜娜丽幸福的世界,而使用GEASS,令人绝对服从的力量,带领黑色骑士团向帝国宣战。带上假面化名ZERO的他,却在一年前的“东京决战”中被好友朱雀击败,被帝国皇帝抹去了记忆。\r\n  现在,恢复记忆的鲁路修不仅要面对帝国的强大军事力量,更要面对虚假的弟弟罗洛、失踪的妹妹娜娜丽、不知敌友的中华联盟、内部出现分歧的黑色骑士团。面对内忧外患,鲁路修走上了GEASS之力的诅咒——孤独的王之路。 ",
      "nsfw": false,
      "date": "2008-04-06",
      "platform": "TV",
      "images": {
        "large": "https://lain.bgm.tv/pic/cover/l/c9/f0/8_wK0z3.jpg",
        "common": "https://lain.bgm.tv/pic/cover/c/c9/f0/8_wK0z3.jpg",
        "medium": "https://lain.bgm.tv/pic/cover/m/c9/f0/8_wK0z3.jpg",
        "small": "https://lain.bgm.tv/pic/cover/s/c9/f0/8_wK0z3.jpg",
        "grid": "https://lain.bgm.tv/pic/cover/g/c9/f0/8_wK0z3.jpg"
      },
      "infobox": [
        {
          "key": "中文名",
          "value": "Code Geass 反叛的鲁路修R2"
        },
        {
          "key": "别名",
          "value": [
            {
              "v": "叛逆的鲁路修R2"
            },
            {
              "v": "Code Geass: Hangyaku no Lelouch R2"
            },
            {
              "v": "叛逆的勒鲁什R2"
            },
            {
              "v": "叛逆的鲁鲁修R2"
            },
            {
              "v": "コードギアス 反逆のルルーシュR2"
            },
            {
              "v": "Code Geass: Lelouch of the Rebellion R2"
            },
            {
              "v": "叛逆的勒路什R2"
            }
          ]
        },
        {
          "key": "话数",
          "value": "25"
        },
        {
          "key": "放送开始",
          "value": "2008年4月6日"
        },
        {
          "key": "官方网站",
          "value": "http://www.geass.jp/r2/"
        },
        {
          "key": "播放电视台",
          "value": "每日放送"
        },
        {
          "key": "播放结束",
          "value": "2008年9月28日"
        },
        {
          "key": "Copyright",
          "value": "(C)2006 SUNRISE inc./MBS"
        },
        {
          "key": "导演",
          "value": "谷口悟朗"
        },
        {
          "key": "系列构成",
          "value": "大河内一楼"
        },
        {
          "key": "人物原案",
          "value": "CLAMP"
        },
        {
          "key": "人物设定",
          "value": "木村貴宏"
        },
        {
          "key": "美术监督",
          "value": "菱沼由典"
        },
        {
          "key": "色彩设计",
          "value": "岩沢れい子"
        },
        {
          "key": "摄影监督",
          "value": "大矢創太"
        },
        {
          "key": "音响监督",
          "value": "浦上靖夫、井澤基"
        },
        {
          "key": "音乐",
          "value": "中川幸太郎、黒石ひとみ"
        },
        {
          "key": "音乐制作",
          "value": "AUDIO PLANNING U"
        },
        {
          "key": "动画制作",
          "value": "サンライズ"
        }
      ],
      "volumes": 0,
      "eps": 25,
      "total_episodes": 25,
      "rating": {
        "rank": 121,
        "total": 77255,
        "count": {
          "1": 44,
          "2": 15,
          "3": 32,
          "4": 66,
          "5": 145,
          "6": 457,
          "7": 1472,
          "8": 3190,
          "9": 2640,
          "10": 1377
        },
        "score": 8.2
      },
      "collection": {
        "wish": 622,
        "collect": 13216,
        "doing": 147,
        "on_hold": 224,
        "dropped": 115
      }
    }
    
    剧集

    GET /v0/episodes

    参数:

    • subject_id 章节关联的条目 id,必需提供的参数。
    • type 0,1,2,3 代表 本篇,sp,op,ed, 默认全部。
    • limit 最大 200
    • offset

    example: /v0/episodes?subject_id=8&limit=5

    {
      "total": 25,
      "limit": 5,
      "offset": 0,
      "data": [
        {
          "id": 522,
          "type": 0,
          "sort": 1,
          "name": "魔神 が 目覚める 日",
          "ep": 1, // 按照sort计算出的剧集数,仅本篇剧集有。sp,op,ed为 null
          "name_cn": "魔王的苏醒之日",
          "duration": "24m",
          "airdate": "2008-04-06",
          "comment": 11,
          "desc": "",
        },
        {
          "id": 523,
          "type": 0,
          "sort": 2,
          "name": "日本独立計画",
          "ep": 2,
          "name_cn": "日本独立计划",
          "duration": "24m",
          "airdate": "2008-04-13",
          "comment": 12,
          "desc": "",
        },
        {
          "id": 524,
          "type": 0,
          "sort": 3,
          "name": "囚われの学園",
          "ep": 3,
          "name_cn": "被囚禁的学园",
          "duration": "24m",
          "airdate": "2008-04-20",
          "comment": 8,
          "desc": "",
        },
        {
          "id": 525,
          "type": 0,
          "sort": 4,
          "name": "逆襲の処刑台",
          "ep": 4,
          "name_cn": "逆行的处刑台",
          "duration": "24m",
          "airdate": "2008-04-27",
          "comment": 20,
          "desc": "",
        },
        {
          "id": 526,
          "type": 0,
          "sort": 5,
          "name": "ナイト オブ ラウンズ",
          "ep": 5,
          "name_cn": "圆桌骑士",
          "duration": "24m",
          "airdate": "2008-05-04",
          "comment": 9,
          "desc": "",
        },
      ],
    }
    
    关联人物

    GET /v0/persons?subject_id={subject_id}

    {
      "total": 1,
      "limit": 30,
      "offset": 0,
      "data": [
        {
          "id": 1,
          "name": "水樹奈々",
          "type": 1,
          "career": ["artist", "seiyu"],
          "images": {
            "large": "https://lain.bgm.tv/pic/crt/l/a6/e8/1_prsn_ZdFfp.jpg?r=1597241889",
            "medium": "https://lain.bgm.tv/pic/crt/m/a6/e8/1_prsn_ZdFfp.jpg?r=1597241889",
            "small": "https://lain.bgm.tv/pic/crt/s/a6/e8/1_prsn_ZdFfp.jpg?r=1597241889",
            "grid": "https://lain.bgm.tv/pic/crt/g/a6/e8/1_prsn_ZdFfp.jpg?r=1597241889"
          },
          "short_summary": "原名 近藤 奈々(こんどう なな),日本女性声优兼歌手。有个妹妹名字是近藤美香,为Daisy×Daisy主唱。\r\n\r\n简介\r\n自小受业余经营歌谣教室的父母影响,",
          "img": "https://lain.bgm.tv/pic/crt/m/a6/e8/1_prsn_ZdFfp.jpg?r=1597241889",
          "locked": false,
          "relation": "开发"
        }
      ]
    }
    
    出场角色

    GET /v0/characters?subject_id={subject_id}

    [
      {
        "id": 1,
        "name": "ルルーシュ・ランペルージ",
        "type": 1,
        "images": {
          "large": "https://lain.bgm.tv/pic/crt/l/7b/3a/1_crt_8V556.jpg?r=1603459589",
          "medium": "https://lain.bgm.tv/pic/crt/m/7b/3a/1_crt_8V556.jpg?r=1603459589",
          "small": "https://lain.bgm.tv/pic/crt/s/7b/3a/1_crt_8V556.jpg?r=1603459589",
          "grid": "https://lain.bgm.tv/pic/crt/g/7b/3a/1_crt_8V556.jpg?r=1603459589"
        },
        "relation": "主角"
      },
      {
        "id": 2,
        "name": "枢木スザク",
        "type": 1,
        "images": {
          "large": "https://lain.bgm.tv/pic/crt/l/6f/40/2_crt_08diJ.jpg?r=1581939778",
          "medium": "https://lain.bgm.tv/pic/crt/m/6f/40/2_crt_08diJ.jpg?r=1581939778",
          "small": "https://lain.bgm.tv/pic/crt/s/6f/40/2_crt_08diJ.jpg?r=1581939778",
          "grid": "https://lain.bgm.tv/pic/crt/g/6f/40/2_crt_08diJ.jpg?r=1581939778"
        },
        "relation": "主角"
      },
      {
        "id": 3,
        "name": "C.C.",
        "type": 1,
        "images": {
          "large": "https://lain.bgm.tv/pic/crt/l/83/62/3_crt_7Jhis.jpg?r=1573280268",
          "medium": "https://lain.bgm.tv/pic/crt/m/83/62/3_crt_7Jhis.jpg?r=1573280268",
          "small": "https://lain.bgm.tv/pic/crt/s/83/62/3_crt_7Jhis.jpg?r=1573280268",
          "grid": "https://lain.bgm.tv/pic/crt/g/83/62/3_crt_7Jhis.jpg?r=1573280268"
        },
        "relation": "主角"
      }
    ]
    
    关联条目

    GET /v0/subjects/{subject_id}/subjects

    [
      {
        "id": 12,
        "type": 2,
        "name": "ちょびっツの「ツ」の字 - Chobits Fan Book",
        "name_cn": "1004",
        "images": {
          "large": "https://lain.bgm.tv/pic/cover/l/65/12/11_bsxG3.jpg",
          "common": "https://lain.bgm.tv/pic/cover/c/65/12/11_bsxG3.jpg",
          "medium": "https://lain.bgm.tv/pic/cover/m/65/12/11_bsxG3.jpg",
          "small": "https://lain.bgm.tv/pic/cover/s/65/12/11_bsxG3.jpg",
          "grid": "https://lain.bgm.tv/pic/cover/g/65/12/11_bsxG3.jpg"
        },
        "relation": "画集"
      },
      {
        "id": 497,
        "type": 1,
        "name": "ちょびっツの「ツ」の字 - Chobits Fan Book",
        "name_cn": "1002",
        "images": {
          "large": "https://lain.bgm.tv/pic/cover/l/65/12/11_bsxG3.jpg",
          "common": "https://lain.bgm.tv/pic/cover/c/65/12/11_bsxG3.jpg",
          "medium": "https://lain.bgm.tv/pic/cover/m/65/12/11_bsxG3.jpg",
          "small": "https://lain.bgm.tv/pic/cover/s/65/12/11_bsxG3.jpg",
          "grid": "https://lain.bgm.tv/pic/cover/g/65/12/11_bsxG3.jpg"
        },
        "relation": "系列"
      }
    ]
    

    close #17 #18 #9

  • 获取编辑历史API设计

    获取编辑历史API设计

    编辑历史API拥有一个公用前缀 /v0/revisions/, 按照不同的编辑种类分成不同的子路由

    • 列出某个条目所有的修订 /v0/revsions/subjects?subject_id=$value
    • 条目编辑历史 /v0/revsions/subjects/{rev_id}

    其他wiki类型如人物/角色/章节类似。

  • 关于获取用户收藏 API 的 limit 参数设计

    关于获取用户收藏 API 的 limit 参数设计

    现状

    获取用户收藏的 GET /v0/users/{username}/collections 其中 limit 参数接收最大值 100,默认值 30。

    当 limit 参数大于 用户总收藏数 时会 Bad Request,产生了无意义的失败请求,徒增 API 使用难度,也为服务器增大了压力。

    {
        "title": "Bad Request",
        "details": {
            "error": "limit should less equal than 40",
            "path": "/v0/users/{username}/collections",
            "query_string": "limit=50&offset=0"
        },
        "description": "limit should less equal than 40"
    }
    

    建议

    1. 新增一个获取用户收藏数的 API,开发者自行避免使用大于总数的 limit 参数进行请求。
    2. 修改逻辑。当 limit 大于用户总收藏数时,返回尽可能多的收藏条目数。

    临时方案

    个人认为当前可行的解决方案,采用一个小于 100 的 limit 参数进行请求,如果出现 Bad Request,从信息中拿到 limit 允许的最大值(即上面 description 中的数字),并进行第二次请求。

  • [Feature Request]: 支持超合金组件

    [Feature Request]: 支持超合金组件

    虽然现在基本不可能考虑这个问题,但姑且发个 Issue。

    你想添加的新功能

    复刻超合金组件。做成类似主站现有的超合金组件就好。 但要是 CSS 能类似 Stylus 一样支持 @-moz-document 关键词以及类似 Tampermonkey 一样提供一些内置命令比如 GM_addstyle 的话就更好了。

    你希望的解决方案

    俺也不知道有什么解决方案,也许已经做出了超合金组件的 Sai 老板知道?

    其他可能的替代方案

    Tampermonkey 可以替代。但是有两个问题: 一是 Tampermonkey 无法跨浏览器共享用户数据,你在PC上使用的插件没法在手机浏览器上启用。 二是与账号无关,登出后仍然会生效。(尽管这样做也有好处。)

    其他相关信息

    希望新站点能让 DOM 解析更简洁!现在构建页面会通过 CSS Modules 处理,导致类名不再固定,这对经常需要解析页面元素的超合金组件实在是不友好。举例:group 页面 中几乎所有元素都有了小尾巴。如果能想办法规避就好了。比如手动加上简单的类名(不考虑重名毕竟CSS不通过它渲染)或 ID,或者能提供前端 API 用于获取页面元素,之类的?

    (补充说明:小尾巴指尾部会有一个 hash 值,比如 _title_gvkfu_51 中的 _gvkfu_51

  • [Feature Request]: 人物/角色/章节的目录收藏

    [Feature Request]: 人物/角色/章节的目录收藏

    目录项目只支持 Subject 类, 对于人物、角色、章节都是错误数据。

    目前针对这三个项目,都是拿其 ID 去 subject 库中找。

    添加到目录,也不支持这三个分类

    Bug2: /v0/indices/{index_id}/subjects 中缺少对类别(Subject,人物,角色,章节)的过滤 (对应数据库 Cat 字段)

  • [Feature Request]: 项目结构调整讨论

    [Feature Request]: 项目结构调整讨论

    个人见解,Web后端重要的在于向外暴露的API和自身数据处理储存,因而有以下一些建议 我个人经验也不十分充足,还请各位大佬多提意见和修改

    • 将所有
      • API相关的(狭义上包括Handler, Middleware,等)
      • 数据库处理相关的(狭义上包括直接操作数据库的GORM相关,MeliSearch相关)

    以及其他同等级别的都放在同一级目录下,取代目前的web和其他组件放于同一级下的结构

    • ... 仅为初步构思,还望各位积极补充,有新的想法了我也会更新该issue
  • get current app name

    get current app name

    https://gist.github.com/Trim21/b930f98e000c7024b41de74f92d192aa#file-timeline-php-L668-L688

    这里的获取用户端类型在重构版中有对应的函数吗

    Originally posted by @TWT233 in https://github.com/bangumi/server/issues/217#issuecomment-1279692516

  • 防止请求重发的中间件

    防止请求重发的中间件

    用户发送请求 A,出于某些原因超时,客户端没有接收到返回。用户选择重发请求,产生请求 B。 服务器会在某个时间范围内收到 A,B 两个一样的请求,理论上应该 drop 第二个请求。

    目前似乎没有这个机制,

    可以对请求的 url ,数据和用户 id,hash一下存到 redis 并设置过期时间 比如 1分钟,在接到第二个请求就直接返回错误。

    之前甚至搞过缓存第一个请求的结果,用户在单位时间内再次请求会返回缓存的结果

    前提自然是第一个请求服务器确实接收到了

    ps: 当然不对 GET 请求生效

  • [Feature Request]:  目录

    [Feature Request]: 目录

    目录的增删查改,包括 我建立的目录某用户的收藏目录

    目前已经存在 /v0/indices/{index_id} /v0/indices/{index_id}/subjects

    已实现 #234

    • [x] 新建目录 POST /indices
    • [x] 修改 PUT /indices/{index_id}
    • [x] 增加内容 POST /indices/{index_id}/subjects
    • [x] 修改内容 PUT /indices/{index_id}/subjects/<subject_id>
    • [x] 删除内容 DELETE /indices/{index_id}/subjects/<subject_id>

    待实现

    • [ ] 收藏目录 POST /indices/{index_id}/collect
    • [ ] 取消收藏 DELETE /indices/{index_id}/collect
    • [ ] 用户的目录 GET /users/{username}/indices
    • [ ] 用户的收藏目录 GET /users/{username}/indices/collect

    暂定不添加

    • [ ] 删除 DELETE /indices/{index_id}
Related tags
HttpRunner+ is the next generation of HttpRunner, written in golang
HttpRunner+ is the next generation of HttpRunner, written in golang

hrp (HttpRunner+) hrp is a golang implementation of HttpRunner. Ideally, hrp will be fully compatible with HttpRunner, including testcase format and u

Sep 28, 2022
Core is the next-generation digital data engine.
Core is the next-generation digital data engine.

tKeel-Core The digital engine of world ?? Core is the data centre of the tKeel IoT Open Platform, a high-performance, scalable and lightweight next-ge

Mar 28, 2022
The server-pubsub is the main backend of DATAVOC project that manages all the other web-server modules of the same project such as the processor

server-pubsub The server-pubsub is the main backend of DATAVOC project that manages all the other web-server modules of the same project such as the p

Dec 3, 2021
Next Terminal是使用Golang和React开发的一款HTML5的远程桌面网关

Next Terminal是使用Golang和React开发的一款HTML5的远程桌面网关,具有小巧、易安装、易使用、资源占用小的特点,支持RDP、SSH、VNC和Telnet协议的连接和管理。

Jun 1, 2021
gh is GitHub on the command line. It brings pull requests, issues, and other GitHub concepts to the terminal next to where you are already working with git and your code
gh is GitHub on the command line. It brings pull requests, issues, and other GitHub concepts to the terminal next to where you are already working with git and your code

gh is GitHub on the command line. It brings pull requests, issues, and other GitHub concepts to the terminal next to where you are already working with git and your code

Jan 24, 2022
Proxy that keeps clients active until the backend server is back online

HoneySmoke HoneySmoke is a prototype proxy for testing until it eventually becomes HoneyHive. HoneySmoke will eventually implement a limbo mode that k

Nov 20, 2021
Go driven rpc code generation tool for right now.
Go driven rpc code generation tool for right now.

Go driven rpc code generation tool for right now. 100% Go Describe services with Go interfaces (with structs, methods, comments, etc.) Generate server

Dec 20, 2022
A simulation to see what's the result among normal people、rich-second generation、hard-working people

A simulation to see what's the result of competion among normal people、rich-second generation and hard-working people. 假设: 一个社会集体中有部分富二代,部分努力的人,多数是普通人

Feb 20, 2022
Build file generation is provided as a plugin for gazelle

JS rules for Bazel Ecosia specific JS Bazel rules to be used with the NodeJS rules Setup http_archive( name = "benchsci_bazel_rules_nodejs_contrib

Dec 14, 2022
The `protoc` compiler plugin which dumps the generation request details

Progotgen DUMP The protoc compiler plugin which dumps the generation request details in "google.golang.org/protobuf/compiler/protogen format to stderr

Jan 23, 2022
llb - It's a very simple but quick backend for proxy servers. Can be useful for fast redirection to predefined domain with zero memory allocation and fast response.

llb What the f--k it is? It's a very simple but quick backend for proxy servers. You can setup redirect to your main domain or just show HTTP/1.1 404

Sep 27, 2022
A Socket.IO backend implementation written in Go

go-socket.io The socketio package is a simple abstraction layer for different web browser- supported transport mechanisms. It is fully compatible with

Sep 25, 2022
A pluggable backend API that enforces the Event Sourcing Pattern for persisting & broadcasting application state changes
A pluggable backend API that enforces the Event Sourcing Pattern for persisting & broadcasting application state changes

A pluggable "Application State Gateway" that enforces the Event Sourcing Pattern for securely persisting & broadcasting application state changes

Nov 1, 2022
webrpc is a schema-driven approach to writing backend services for modern Web apps and networks
webrpc is a schema-driven approach to writing backend services for modern Web apps and networks

webrpc is a schema-driven approach to writing backend servers for the Web. Write your server's api interface in a schema format of RIDL or JSON, and t

Jan 7, 2023
A Xray backend framework that can easily support many panels. 一个基于Xray的后端框架,支持V2ay,Trojan,Shadowsocks协议,极易扩展,支持多面板对接

XRayR A Xray backend framework that can easily support many panels. 一个基于Xray的后端框架,支持V2ay,Trojan,Shadowsocks协议,极易扩展,支持多面板对接。 如果您喜欢本项目,可以右上角点个star+watch

Jan 4, 2023
protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript clients that connect the web frontend and golang backend fronted by grpc-gateway.

protoc-gen-grpc-gateway-ts protoc-gen-grpc-gateway-ts is a Typescript client generator for the grpc-gateway project. It generates idiomatic Typescript

Dec 19, 2022
Findmentor.network backend project

Build Setup go get github.com/findmentor-network/backend make build <$ ./backend Findmentor API Usage: backend [command] Available Commands: a

Jan 28, 2022
TwitchHub Golang Backend

TwitchHub Golang Backend setup development environment: clone the repository

Oct 25, 2022
Trello backend repo for Ladno Davayte Bez Roflov team

Trello Trello backend repository for Ladno Davayte Bez Roflov team, autumn of 2021. Team Anton Chumakov; Alexander Orletskiy; Georgij Sedojkin; Dmitri

Dec 25, 2021