├── .env.docker.compose
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.md
│ └── feature_request.md
└── workflows
│ ├── devcheck.yml
│ ├── docker-push.yml
│ ├── lint.yml
│ ├── maincheck.yml
│ └── qodana.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── CONTRIBUTING_CN.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── config
└── logs
│ ├── fluent-bit.conf
│ ├── parsers.conf
│ └── readme.md
├── docker-compose.yaml
├── docker
├── basic
│ ├── Dockerfile
│ └── readme.md
├── clash
│ ├── Dockerfile
│ └── readme.md
└── rabbitmq
│ └── Dockerfile
├── docs
└── README-CN.md
├── go.mod
├── go.sum
├── gorse-config.docker.compose.toml
├── manifests-endymx
├── configmap.yaml
├── deployment-auth-service.yaml
├── deployment-comment-service.yaml
├── deployment-event-service.yaml
├── deployment-favorite-service.yaml
├── deployment-feed-service.yaml
├── deployment-http-service.yaml
├── deployment-message-service.yaml
├── deployment-msg-consumer-service.yaml
├── deployment-publish-service.yaml
├── deployment-recommend-service.yaml
├── deployment-relation-service.yaml
├── deployment-user-service.yaml
├── deployment-video-processor-service.yaml
└── sevice-http-api.yaml
├── promethus.docker.compose.yml
├── scripts
├── build-all.bat
├── build-all.ps1
├── build-all.sh
└── run-all.sh
├── src
├── constant
│ ├── config
│ │ ├── .env.example
│ │ ├── env.go
│ │ └── service.go
│ └── strings
│ │ ├── common.go
│ │ ├── err.go
│ │ └── service.go
├── extra
│ ├── gorse
│ │ ├── client.go
│ │ └── model.go
│ ├── profiling
│ │ └── analyzer.go
│ └── tracing
│ │ └── otel.go
├── idl
│ ├── auth.proto
│ ├── chat.proto
│ ├── check.proto
│ ├── comment.proto
│ ├── favorite.proto
│ ├── feed.proto
│ ├── publish.proto
│ ├── recommand.proto
│ ├── relation.proto
│ └── user.proto
├── models
│ ├── action.go
│ ├── comment.go
│ ├── event.go
│ ├── message.go
│ ├── rawvideo.go
│ ├── relation.go
│ ├── user.go
│ └── video.go
├── rpc
│ ├── auth
│ │ ├── auth.pb.go
│ │ └── auth_grpc.pb.go
│ ├── chat
│ │ ├── chat.pb.go
│ │ └── chat_grpc.pb.go
│ ├── comment
│ │ ├── comment.pb.go
│ │ └── comment_grpc.pb.go
│ ├── favorite
│ │ ├── favorite.pb.go
│ │ └── favorite_grpc.pb.go
│ ├── feed
│ │ ├── feed.pb.go
│ │ └── feed_grpc.pb.go
│ ├── health
│ │ ├── check.pb.go
│ │ └── check_grpc.pb.go
│ ├── publish
│ │ ├── publish.pb.go
│ │ └── publish_grpc.pb.go
│ ├── recommend
│ │ ├── recommand.pb.go
│ │ └── recommand_grpc.pb.go
│ ├── relation
│ │ ├── relation.pb.go
│ │ └── relation_grpc.pb.go
│ └── user
│ │ ├── user.pb.go
│ │ └── user_grpc.pb.go
├── services
│ ├── auth
│ │ ├── handler.go
│ │ └── main.go
│ ├── comment
│ │ ├── handler.go
│ │ ├── main.go
│ │ └── moderation.go
│ ├── event
│ │ └── main.go
│ ├── favorite
│ │ ├── handler.go
│ │ └── main.go
│ ├── feed
│ │ ├── handler.go
│ │ └── main.go
│ ├── message
│ │ ├── handler.go
│ │ └── main.go
│ ├── msgconsumer
│ │ ├── esexchange.go
│ │ └── main.go
│ ├── publish
│ │ ├── handler.go
│ │ └── main.go
│ ├── recommend
│ │ ├── handler.go
│ │ └── main.go
│ ├── relation
│ │ ├── handler.go
│ │ └── main.go
│ ├── user
│ │ ├── handler.go
│ │ └── main.go
│ └── videoprocessor
│ │ ├── main.go
│ │ └── summary.go
├── storage
│ ├── cached
│ │ ├── cache.go
│ │ └── ticker.go
│ ├── database
│ │ └── gorm.go
│ ├── es
│ │ └── Elasticsearch.go
│ ├── file
│ │ ├── fs.go
│ │ └── provider.go
│ ├── mq
│ │ └── rabbitmq.go
│ └── redis
│ │ └── redis.go
├── utils
│ ├── audit
│ │ └── publish.go
│ ├── consul
│ │ └── register.go
│ ├── grpc
│ │ └── connection.go
│ ├── logging
│ │ ├── gorm.go
│ │ └── logging.go
│ ├── pathgen
│ │ └── video.go
│ ├── prom
│ │ ├── interceptor.go
│ │ └── pack.go
│ ├── ptr
│ │ └── ptr.go
│ └── rabbitmq
│ │ ├── mq.go
│ │ └── otel.go
└── web
│ ├── about
│ └── handler.go
│ ├── auth
│ └── handler.go
│ ├── comment
│ └── handler.go
│ ├── favorite
│ └── handler.go
│ ├── feed
│ └── handler.go
│ ├── main.go
│ ├── message
│ └── handler.go
│ ├── middleware
│ ├── authenticate.go
│ └── limiter.go
│ ├── models
│ ├── About.go
│ ├── Comment.go
│ ├── Favorite.go
│ ├── Feed.go
│ ├── Login.go
│ ├── Message.go
│ ├── Publish.go
│ ├── Relation.go
│ └── User.go
│ ├── publish
│ └── handler.go
│ ├── relation
│ └── handler.go
│ ├── user
│ └── handler.go
│ └── utils
│ └── json.go
└── test
├── k6
├── comment.js
├── comment_get.js
├── favorite.js
├── favorite_random.js
└── feed.js
├── rpc
├── authrpc_test.go
├── commentrpc_test.go
├── feedrpc_test.go
├── like_test.go
├── messagerpc_test.go
├── publishrpc_test.go
├── relationrpc_test.go
└── userrpc_test.go
└── web
├── auth_test.go
├── comment_test.go
├── favorite_test.go
├── feed_test.go
├── message_test.go
├── publish_test.go
├── relation_test.go
└── value.go
/.env.docker.compose:
--------------------------------------------------------------------------------
1 | # Configure Consul address, the default address is `localhost:8500`
2 | # TIPS: If you provide `CONSUL_ANONYMITY_NAME`, all services will register with `CONSUL_ANONYMITY_NAME` as prefix
3 | CONSUL_ADDR=consul:8500
4 | CONSUL_ANONYMITY_NAME=paraparty.
5 | # Configure logger level, support: DEBUG, INFO, WARN (WARNING), ERROR, FATAL
6 | LOGGER_LEVEL=INFO
7 | # Cofigure logger integrated with otel, support: enable, disable
8 | # If this setting is enable, you will see log in the OTEL Export with possible runtime waste
9 | LOGGER_OUT_TRACING=enable
10 | # Configure Tied information, which will be bound with every log print
11 | TIED=
12 | # Configure PostgreSQL connection information
13 | # You can just provide conn, and the program will auto migrate data
14 | # If you do not provide PostgreSQL schema, this field would not take effect without any error
15 | POSTGRESQL_HOST=rdb
16 | POSTGRESQL_PORT=5432
17 | POSTGRESQL_USER=gugotik
18 | POSTGRESQL_PASSWORD=gugotik123
19 | POSTGRESQL_DATABASE=gugodb
20 | POSTGRESQL_SCHEMA=public
21 | POSTGRESQL_PREFIX=gugotik_
22 | # Configure storage mode, support: fs, s3
23 | # fs: stoarge binary files in the local machine, use this should provide `FS_PATH` config, or will output at /tmp. Aslo,
24 | # you should provide `FS_BASEURL`, the default is `http://localhost/`
25 | # s3: I do not know what is s3, do not ask me plz.
26 | STORAGE_TYPE=fs
27 | FS_PATH=/usr/share/nginx/html/
28 | FS_BASEURL=http://192.168.124.33:8066/
29 | # Configure redis host
30 | # `REDIS_PASSWORD` has a default value ''
31 | # `REDIS_DB` has a default value '0'
32 | # `REDIS_PREFIX` will make field `PREFIX-KEYNAME` style
33 | # TIPS: There is a Auto choose mode for Redis
34 | # TIPS: You can opt to use `Single Redis Node` with providing a single ip
35 | # TIPS: You can opt to use `Redis Cluster` with providing multi redis using ';' to split
36 | # TIPS: When you trying to use Redis Cluster, you should ensure they have the same password or have no password
37 | # TIPS: If you do not provide the name of REDIS_MASTER, the Redis client will use normal way get addr of REDIS SERVER
38 | REDIS_PREFIX=GuGoTik
39 | REDIS_ADDR=redis:6379
40 | REDIS_PASSWORD=
41 | REDIS_DB=
42 | REDIS_MASTER=
43 | # Config Tracing EndPoint, support Jaeger
44 | # Config state, if use `disable` the sampler will be closed. use `enable` to enable
45 | TRACING_STATE=enable
46 | # Config tracing sampler, suggest 0.01
47 | TRACING_SAMPLER=0.1
48 | TRACING_ENDPOINT=jaeger:4318
49 | # Optional: Config Pyroscope
50 | # Decide whether to enable the service, support : enable, disable.
51 | # If you enable this service, you must provide Pyroscope server environment
52 | # This profiling is ONLY designed for DEBUGGING
53 | # SO, PLEASE DO NOT ENABLE THIS SERVICE IN YOUR PRODUCTION ENVIRONMENT, OR IT MAY TAKE MUCH RUNTIME COST.
54 | PYROSCOPE_STATE=enable
55 | PYROSCOPE_ADDR=http://pyroscope:4040/
56 | # Configure RabbitMQ
57 | # Optional: `RABBITMQ_VHOST_PREFIX`: If you provide this config, the service will use value as the rabbit mq vhost prefix.
58 | # The default value of `RABBITMQ_VHOST_PREFIX` is empty, so if the service use `/post`, the real host is `/post` also.
59 | # ATTENTION: The value of `RABBITMQ_VHOST_PREFIX` is "path/to/your/host" like, such as `gugotik`, but not `/gugotik`
60 | RABBITMQ_USERNAME=guest
61 | RABBITMQ_PASSWORD=guest
62 | RABBITMQ_ADDRESS=rabbitmq
63 | RABBITMQ_PORT=5672
64 | RABBITMQ_VHOST_PREFIX=
65 | # ChatGPT API secret key
66 | CHATGPT_API_KEYS=
67 | # Gorse provides recommend service for GuGoTik.
68 | GORSE_ADDR=http://gorse-server:8087
69 | GORSE_APIKEY=5105502fc46a411c896aa5b50c31e951
70 | ANONYMITY_USER=50
71 | # Configure your Elastic Search Address
72 | ES_ADDR=http://elasticsearch:9200
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Report a bug of GuGoTik
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: liaosunny123
7 |
8 | ---
9 |
10 | **Component**
11 | The component of bug is: xxx
12 |
13 | **Describe the bug**
14 | A clear and concise description of what the bug is.
15 |
16 | **To Reproduce**
17 | Steps to reproduce the behavior.
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Additional context**
26 | Add any other context about the problem here.
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for GuGoTik
4 | title: "[Feature]"
5 | labels: enhancement
6 | assignees: liaosunny123
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
22 | **Do you want to PR?**
23 | Yes ! / No !
24 |
--------------------------------------------------------------------------------
/.github/workflows/devcheck.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: DevBuildCheck
5 |
6 | on:
7 | push:
8 | branches: [ "dev" ]
9 | pull_request:
10 | branches: [ "dev" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.20'
23 |
24 | - name: Build
25 | run: go build -v ./...
26 |
--------------------------------------------------------------------------------
/.github/workflows/docker-push.yml:
--------------------------------------------------------------------------------
1 | name: Docker Push
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | branches: [ "main" ]
7 |
8 | jobs:
9 | docker:
10 | runs-on: ubuntu-latest
11 | environment: Docker
12 | steps:
13 | -
14 | name: Set up QEMU
15 | uses: docker/setup-qemu-action@v2
16 | -
17 | name: Set up Docker Buildx
18 | uses: docker/setup-buildx-action@v2
19 | -
20 | name: Login to Docker Hub
21 | uses: docker/login-action@v2
22 | with:
23 | username: ${{ secrets.DOCKERHUB_USERNAME }}
24 | password: ${{ secrets.DOCKERHUB_TOKEN }}
25 | -
26 | name: Build and push
27 | uses: docker/build-push-action@v4
28 | with:
29 | push: true
30 | tags: |
31 | ${{ secrets.DOCKERHUB_USERNAME }}/gugotik:latest
32 | ${{ secrets.DOCKERHUB_USERNAME }}/gugotik:${{ github.sha }}
33 |
--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
1 | name: Lint
2 | on:
3 | push:
4 | branches:
5 | - dev
6 | - main
7 | pull_request:
8 | branches:
9 | - dev
10 | - main
11 |
12 | permissions:
13 | contents: read
14 | # Optional: allow read access to pull request. Use with `only-new-issues` option.
15 | # pull-requests: read
16 |
17 | jobs:
18 | golangci:
19 | name: lint
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v3
23 | - uses: actions/setup-go@v4
24 | with:
25 | go-version: '1.20'
26 | cache: false
27 | - name: golangci-lint
28 | uses: golangci/golangci-lint-action@v3
29 | with:
30 | # Require: The version of golangci-lint to use.
31 | # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version.
32 | # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit.
33 | version: v1.53
--------------------------------------------------------------------------------
/.github/workflows/maincheck.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a golang project
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
3 |
4 | name: MainBuildCheck
5 |
6 | on:
7 | push:
8 | branches: [ "main" ]
9 | pull_request:
10 | branches: [ "main" ]
11 |
12 | jobs:
13 |
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: actions/checkout@v3
18 |
19 | - name: Set up Go
20 | uses: actions/setup-go@v4
21 | with:
22 | go-version: '1.20'
23 |
24 | - name: Build
25 | run: go build -v ./...
26 |
--------------------------------------------------------------------------------
/.github/workflows/qodana.yml:
--------------------------------------------------------------------------------
1 | name: Code Scan
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | branches: [ "dev" ]
6 |
7 | jobs:
8 | qodana:
9 | runs-on: ubuntu-latest
10 | environment: Analysis
11 | steps:
12 | - uses: actions/checkout@v3
13 | with:
14 | fetch-depth: 0
15 | - name: 'Qodana Scan'
16 | uses: JetBrains/qodana-action@v2023.2
17 | with:
18 | pr-mode: false
19 | args: --apply-fixes
20 | push-fixes: pull-request
21 | upload-result: true
22 | env:
23 | QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
24 | - uses: github/codeql-action/upload-sarif@v2
25 | with:
26 | sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | debug/
3 | output/
4 | log/
5 | .env
6 | docker/basic/static
7 | docker/clash/static
8 | docker/rabbitmq/static
9 | static/
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # contribute
2 | We welcome anyone to contribute to GuGoTik, and look forward to your contributions to GuGoTik, whether it is bug fixes or feature contributions!
3 | [中文简体](CONTRIBUTING_CN.md)
4 | # Contribution process
5 | - Before you start contributing to something, please submit an Issue and describe in detail what you are doing
6 | For example: if you want to fix a bug, you need to raise the bug issue first, and then comment that you are working on this, so that we can Assign the task to you.
7 | - When you are contributing, please make sure to follow GuGoTik's original project code style, and complete GoLint operations and single-test operations locally.
8 | - When you have completed your code modification, please submit it to the repository in the form of a PR and mention your Issue for us to track.
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING_CN.md:
--------------------------------------------------------------------------------
1 | # 贡献
2 | 我们欢迎任何人参与到 GuGoTik 的贡献中,并且期待你们对于 GuGoTik 无论是 Bug 修复,还是 Feature 贡献等任何 Contribution !
3 | # 贡献流程
4 | - 在你开始参与某方面的贡献前,请先提交一个 Issue,并且详细描述你正在进行的行为
5 | 例如:如果你要修复某个 Bug,需要先提出 Bug Issue,然后评论你正在进行这方面的工作,以供我们 Assign 任务到你。
6 | - 当你正在贡献的时候,请确保遵守了 GuGoTik 原有的项目代码风格,并且在本地完成 GoLint 操作和单测操作。
7 | - 当你完成的你的代码修改后,请以 PR 的形式提交到仓库,并提及你的 Issue,以供我们追踪。
8 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:alpine as builder
2 |
3 | WORKDIR /build
4 |
5 | ENV CGO_ENABLED 0
6 | ENV GOPROXY https://goproxy.cn/,direct
7 |
8 | RUN apk update --no-cache \
9 | && apk upgrade \
10 | && apk add --no-cache bash \
11 | bash-doc \
12 | bash-completion \
13 | && apk add --no-cache tzdata \
14 | && rm -rf /var/cache/apk/*
15 |
16 | COPY . .
17 |
18 | RUN go mod download \
19 | && bash ./scripts/build-all.sh
20 |
21 | FROM docker.io/epicmo/gugotik-basic:1.3 as prod
22 |
23 | ENV TZ Asia/Shanghai
24 |
25 | WORKDIR /data/apps/gugotik-service-bundle
26 |
27 | RUN apk update --no-cache \
28 | && apk upgrade
29 |
30 | COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
31 | COPY --from=builder /build/output .
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 2.0.x | :white_check_mark: |
8 | | 1.0.x | :white_check_mark: |
9 |
10 | ## Reporting a Vulnerability
11 |
12 | If you find a vulnerability, please report us using a issue.
13 |
--------------------------------------------------------------------------------
/config/logs/fluent-bit.conf:
--------------------------------------------------------------------------------
1 | [SERVICE]
2 | Parsers_File parsers.conf
3 | Daemon Off
4 | Log_Level info
5 | HTTP_Server off
6 | HTTP_Listen 0.0.0.0
7 | HTTP_Port 24224
8 | [INPUT]
9 | Name tail
10 | Tag gugotik.*
11 | Path /var/log/gugotik/*.log
12 | Mem_Buf_Limit 10MB
13 | DB /var/log/flt_logs.db
14 | Refresh_Interval 5
15 | Ignore_Older 10s
16 | Rotate_Wait 5
17 | [FILTER]
18 | Name record_modifier
19 | Match *
20 | Key_name message
21 | Record hostname ${HOSTNAME}
22 | Record namespace gugotik
23 | Record environment prod
24 | [OUTPUT]
25 | Name es
26 | Match *
27 | Host [YOUR HOST]
28 | Port 9200
29 | Logstash_Format On
30 | Retry_Limit False
31 | Time_Key @timestamp
32 | Logstash_Prefix gugotik-log
--------------------------------------------------------------------------------
/config/logs/parsers.conf:
--------------------------------------------------------------------------------
1 | [PARSER]
2 | Name docker
3 | Format json
4 | Time_Key time
5 | Time_Format %Y-%m-%dT%H:%M:%SZ
6 | Time_Keep On
--------------------------------------------------------------------------------
/config/logs/readme.md:
--------------------------------------------------------------------------------
1 | # Log Solution
2 |
3 | GuGoTik 支持以 Fluent-bit 以 Sidecar 模式收集日志,目录中为推荐配置方案,开箱即用。
4 |
5 | # Use
6 |
7 | 请根据需要自行更改 ES 的用户账号和密码等
--------------------------------------------------------------------------------
/docker/basic/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine
2 |
3 | WORKDIR /data/apps/gugotik-service-bundle/static
4 |
5 | RUN apk update --no-cache \
6 | && apk upgrade \
7 | && apk add yasm \
8 | && apk add ffmpeg \
9 | && rm -rf /var/cache/apk/*
10 |
11 | COPY ./static .
--------------------------------------------------------------------------------
/docker/basic/readme.md:
--------------------------------------------------------------------------------
1 | # 说明
2 | 本镜像为 GuGoTik 提供基础镜像层服务
3 | # 功能
4 | - 提供 FFmpeg 环境
5 | - 将 static 目录下的 font.ttf 作为 GuGoTik 的水印标记
6 | # 集成
7 | 本镜像推送后,将作为主 Dockerfile 的 prod 基础镜像,如果修改了请同步修改主 Dockerfile
--------------------------------------------------------------------------------
/docker/clash/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.18.2
2 |
3 | WORKDIR /clash
4 | COPY ./static/clash /clash
5 | COPY ./static/Country.mmdb /root/.config/clash
6 | COPY ./static/config.yaml /root/.config/clash
--------------------------------------------------------------------------------
/docker/clash/readme.md:
--------------------------------------------------------------------------------
1 | # 说明
2 | 本镜像为 GuGoTik 提供透明网络代理功能
3 | # 功能
4 | - 提供 OpenAI 访问
5 | # 集成
6 | 本镜像推送后,将作为 VideoProcess / Comment / MessageConsumer / Message 的透明网络镜像,请自行作为透明网络代理
7 |
--------------------------------------------------------------------------------
/docker/rabbitmq/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rabbitmq:3.12.3-management
2 |
3 | COPY ./static /plugins
4 |
5 | RUN rabbitmq-plugins enable rabbitmq_delayed_message_exchange
--------------------------------------------------------------------------------
/docs/README-CN.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # GuGoTik
10 |
11 | _✨ 第六届字节跳动青训营进阶班后端实战项目第一名,迷你抖音后端 GuGoTik ✨_
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | 下载
29 | ·
30 | 参与贡献
31 | ·
32 | 文档
33 |
34 |
35 |
36 |
37 |
GIVE US A STAR PLEASE MY SIR !!! | 请给我们一个 Star 求求了 !!!
38 |
39 |
40 | GuGoTik是 第六届字节跳动青训营后端进阶 实战项目,题目为编写一个小型的抖音后端。
41 |
42 | 如果你想了解更多信息,请等待 青训营结束后 ,GuGoTik 会提供完整的项目开发文档,请给本项目一个 Star ~
43 | # 贡献者
44 | 项目开发者:这是一群来自五湖四海的 Contributors,来自于 WHU,HNU,NJUPT。
45 | - [EpicMo](https://github.com/liaosunny123)
46 | - [Maples](https://github.com/Maple-pro)
47 | - [Attacks](https://github.com/Attack825)
48 | - [amazing-compass](https://github.com/amazing-compass)
49 | - [XFFFCCCC](https://github.com/XFFFCCCC)
50 |
51 | 特别感谢:
52 | - [Eric](https://github.com/ExerciseBook)
53 | - [Huang Yongliang](https://github.com/956237586)
54 | - [nicognaW](https://github.com/nicognaW)
55 |
56 | 以及有事而无法参与项目的小伙伴:
57 | - [Chuanwise](https://github.com/Chuanwise)
58 |
59 | # 项目结构
60 | - docker: 基础镜像,为项目的Dockerfile提供基础镜像,或者是为 K8S 技术设施提供基础镜像
61 | - scripts: 构建脚本
62 | - src: 项目源代码
63 | - constant: 项目常量
64 | - extra: 外部服务依赖
65 | - idl: idl文件
66 | - models: 数据模型
67 | - rpc: Rpc 代码
68 | - services: 微服务实例
69 | - storage: 存储相关
70 | - utils: 辅助代码
71 | - web: 网关代码
72 | - test: 项目测试
73 | - 其他单文件:Docker Compose 文件和使用的demo环境变量
74 |
75 | # 外部服务依赖
76 | - Redis (Cluster)
77 | - PostgreSQL
78 | - Consul
79 | - OpenTelemetry Collector
80 | - FFMpeg
81 | - Go
82 |
83 | 项目推荐使用以下可观测性基础设施:
84 | - Jaeger
85 | - Victoria Metrics
86 | - Grafana
87 |
88 | Profile 性能分析:
89 | - Pyroscope
90 |
91 | # 自部署流程
92 | 由 梦想珈 RyzeBot 提供自动推送至K8S集群构建流程。
93 | PR 至 Dev 分支,经过基于 Action 的 UnitTest + Code Analysis + Lint + BuildCheck 后,可合并至 endymx 分支。
94 | endymx 分支会自动触发 CD,构建镜像并推送,由 RyzeBot 完成向 K8S 的推送,自动部署。
95 |
96 | # 配置
97 | GuGoTik可以自动捕获环境变量,也可以以 .env 文件的方式手动提供,覆盖顺序为:
98 | .env > 环境变量 > DefaultEnv > EmptyEnv(即默认提供空值,由GuGoTik提供运行时特判)
99 |
100 | # 构建
101 | ## 基于 Standalone
102 | 运行 scripts 文件夹下 build-all 脚本,然后运行 run-all 脚本即可,请选择自己平台支持的脚本。
103 | ## 基于 Docker
104 | ```bash
105 | docker pull epicmo/gugotik:latest
106 | ```
107 | 通过交互式终端进入容器后自行运行 GateWay 文件夹下和 Services 文件夹下程序
108 | ## 基于 Docker-Compose
109 | 在项目根目录运行:
110 | 注:相关的账号密码设置在 .env.docker.compose 文件查看
111 | ```bash
112 | docker compose up -d
113 | ```
114 |
--------------------------------------------------------------------------------
/manifests-endymx/configmap.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: env-config
5 | namespace: gugotik-service-bundle
--------------------------------------------------------------------------------
/manifests-endymx/deployment-auth-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-auth-service
8 | name: gugotik-auth-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-auth-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-auth-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-auth-service
21 | dream-app: gugotik-auth-service
22 | dream-unit: gugotik-auth-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-auth-service
30 | command:
31 | - ./services/auth/AuthService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | ports:
40 | - name: grpc-37001
41 | containerPort: 37001
42 | protocol: TCP
43 | - name: metrics-37099
44 | containerPort: 37099
45 | protocol: TCP
46 | volumeMounts:
47 | - mountPath: /var/log/gugotik
48 | name: log-volume
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-comment-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-comment-service
8 | name: gugotik-comment-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-comment-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-comment-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-comment-service
21 | dream-app: gugotik-comment-service
22 | dream-unit: gugotik-comment-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-comment-service
30 | command:
31 | - ./services/comment/CommentService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37003
44 | containerPort: 37003
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-event-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-event-service
8 | name: gugotik-event-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-event-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-event-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-event-service
21 | dream-app: gugotik-event-service
22 | dream-unit: gugotik-event-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-event-service
30 | command:
31 | - ./services/event/EventService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: metrics-37099
44 | containerPort: 37099
45 | protocol: TCP
46 | resources:
47 | limits:
48 | cpu: 2000m
49 | memory: 2048Mi
50 | requests:
51 | cpu: 100m
52 | memory: 128Mi
53 | - name: logger
54 | image: fluent/fluent-bit:1.8.4
55 | imagePullPolicy: IfNotPresent
56 | resources:
57 | requests:
58 | cpu: 20m
59 | memory: 100Mi
60 | limits:
61 | cpu: 100m
62 | memory: 200Mi
63 | volumeMounts:
64 | - mountPath: /fluent-bit/etc
65 | name: config
66 | - mountPath: /var/log/gugotik
67 | name: log-volume
68 | volumes:
69 | - name: config
70 | configMap:
71 | name: gugotik-log-config
72 | - name: log-volume
73 | emptyDir: { }
74 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-favorite-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-favorite-service
8 | name: gugotik-favorite-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-favorite-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-favorite-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-favorite-service
21 | dream-app: gugotik-favorite-service
22 | dream-unit: gugotik-favorite-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-favorite-service
30 | command:
31 | - ./services/favorite/FavoriteService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37006
44 | containerPort: 37006
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-feed-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-feed-service
8 | name: gugotik-feed-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-feed-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-feed-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-feed-service
21 | dream-app: gugotik-feed-service
22 | dream-unit: gugotik-feed-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-feed-service
30 | command:
31 | - ./services/feed/FeedService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37004
44 | containerPort: 37004
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-http-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-http-service
8 | name: gugotik-http-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-http-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-http-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-http-service
21 | dream-app: gugotik-http-service
22 | dream-unit: gugotik-http-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-http-service
30 | command:
31 | - ./gateway/Gateway
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: http-37000
44 | containerPort: 37000
45 | protocol: TCP
46 | resources:
47 | limits:
48 | cpu: 2000m
49 | memory: 2048Mi
50 | requests:
51 | cpu: 100m
52 | memory: 128Mi
53 | - name: logger
54 | image: fluent/fluent-bit:1.8.4
55 | imagePullPolicy: IfNotPresent
56 | resources:
57 | requests:
58 | cpu: 20m
59 | memory: 100Mi
60 | limits:
61 | cpu: 100m
62 | memory: 200Mi
63 | volumeMounts:
64 | - mountPath: /fluent-bit/etc
65 | name: config
66 | - mountPath: /var/log/gugotik
67 | name: log-volume
68 | volumes:
69 | - name: config
70 | configMap:
71 | name: gugotik-log-config
72 | - name: log-volume
73 | emptyDir: { }
74 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-message-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-message-service
8 | name: gugotik-message-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-message-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-message-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-message-service
21 | dream-app: gugotik-message-service
22 | dream-unit: gugotik-message-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-message-service
30 | command:
31 | - ./services/message/MessageService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37007
44 | containerPort: 37007
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-msg-consumer-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-msgconsumer-service
8 | name: gugotik-msgconsumer-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-msgconsumer-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-msgconsumer-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-msgconsumer-service
21 | dream-app: gugotik-msgconsumer-service
22 | dream-unit: gugotik-msgconsumer-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-msgconsumer-service
30 | command:
31 | - ./services/msgconsumer/MsgconsumerService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: metrics-37099
44 | containerPort: 37099
45 | protocol: TCP
46 | resources:
47 | limits:
48 | cpu: 2000m
49 | memory: 2048Mi
50 | requests:
51 | cpu: 100m
52 | memory: 128Mi
53 | - name: logger
54 | image: fluent/fluent-bit:1.8.4
55 | imagePullPolicy: IfNotPresent
56 | resources:
57 | requests:
58 | cpu: 20m
59 | memory: 100Mi
60 | limits:
61 | cpu: 100m
62 | memory: 200Mi
63 | volumeMounts:
64 | - mountPath: /fluent-bit/etc
65 | name: config
66 | - mountPath: /var/log/gugotik
67 | name: log-volume
68 | volumes:
69 | - name: config
70 | configMap:
71 | name: gugotik-log-config
72 | - name: log-volume
73 | emptyDir: { }
74 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-publish-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-publish-service
8 | name: gugotik-publish-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-publish-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-publish-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-publish-service
21 | dream-app: gugotik-publish-service
22 | dream-unit: gugotik-publish-service
23 | spec:
24 | volumes:
25 | - name: volume
26 | persistentVolumeClaim:
27 | claimName: storage
28 | - name: config
29 | configMap:
30 | name: gugotik-log-config
31 | - name: log-volume
32 | emptyDir: { }
33 | imagePullSecrets:
34 | - name: regcred
35 | containers:
36 | - image: ${IMAGE}
37 | imagePullPolicy: IfNotPresent
38 | name: gugotik-publish-service
39 | command:
40 | - ./services/publish/PublishService
41 | envFrom:
42 | - configMapRef:
43 | name: env-config
44 | - configMapRef:
45 | name: gugotik-env
46 | - secretRef:
47 | name: gugotik-secret
48 | ports:
49 | - name: grpc-37005
50 | containerPort: 37005
51 | protocol: TCP
52 | - name: metrics-37099
53 | containerPort: 37099
54 | protocol: TCP
55 | resources:
56 | limits:
57 | cpu: 2000m
58 | memory: 2048Mi
59 | requests:
60 | cpu: 100m
61 | memory: 128Mi
62 | volumeMounts:
63 | - mountPath: /data/apps/gugotik-service-bundle/data
64 | name: volume
65 | - mountPath: /var/log/gugotik
66 | name: log-volume
67 | - name: logger
68 | image: fluent/fluent-bit:1.8.4
69 | imagePullPolicy: IfNotPresent
70 | resources:
71 | requests:
72 | cpu: 20m
73 | memory: 100Mi
74 | limits:
75 | cpu: 100m
76 | memory: 200Mi
77 | volumeMounts:
78 | - mountPath: /fluent-bit/etc
79 | name: config
80 | - mountPath: /var/log/gugotik
81 | name: log-volume
82 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-recommend-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-recommend-service
8 | name: gugotik-recommend-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-recommend-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-recommend-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-recommend-service
21 | dream-app: gugotik-recommend-service
22 | dream-unit: gugotik-recommend-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-recommend-service
30 | command:
31 | - ./services/recommend/RecommendService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37009
44 | containerPort: 37009
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-relation-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-relation-service
8 | name: gugotik-relation-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-relation-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-relation-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-relation-service
21 | dream-app: gugotik-relation-service
22 | dream-unit: gugotik-relation-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-relation-service
30 | command:
31 | - ./services/relation/RelationService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37008
44 | containerPort: 37008
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-user-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-user-service
8 | name: gugotik-user-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-user-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-user-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-user-service
21 | dream-app: gugotik-user-service
22 | dream-unit: gugotik-user-service
23 | spec:
24 | imagePullSecrets:
25 | - name: regcred
26 | containers:
27 | - image: ${IMAGE}
28 | imagePullPolicy: IfNotPresent
29 | name: gugotik-user-service
30 | command:
31 | - ./services/user/UserService
32 | envFrom:
33 | - configMapRef:
34 | name: env-config
35 | - configMapRef:
36 | name: gugotik-env
37 | - secretRef:
38 | name: gugotik-secret
39 | volumeMounts:
40 | - mountPath: /var/log/gugotik
41 | name: log-volume
42 | ports:
43 | - name: grpc-37002
44 | containerPort: 37002
45 | protocol: TCP
46 | - name: metrics-37099
47 | containerPort: 37099
48 | protocol: TCP
49 | resources:
50 | limits:
51 | cpu: 2000m
52 | memory: 2048Mi
53 | requests:
54 | cpu: 100m
55 | memory: 128Mi
56 | - name: logger
57 | image: fluent/fluent-bit:1.8.4
58 | imagePullPolicy: IfNotPresent
59 | resources:
60 | requests:
61 | cpu: 20m
62 | memory: 100Mi
63 | limits:
64 | cpu: 100m
65 | memory: 200Mi
66 | volumeMounts:
67 | - mountPath: /fluent-bit/etc
68 | name: config
69 | - mountPath: /var/log/gugotik
70 | name: log-volume
71 | volumes:
72 | - name: config
73 | configMap:
74 | name: gugotik-log-config
75 | - name: log-volume
76 | emptyDir: { }
77 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/deployment-video-processor-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | annotations:
5 | sidecar.jaegertracing.io/inject: 'false'
6 | labels:
7 | app: gugotik-video-service
8 | name: gugotik-video-service
9 | namespace: gugotik-service-bundle
10 | spec:
11 | selector:
12 | matchLabels:
13 | name: gugotik-video-service
14 | template:
15 | metadata:
16 | labels:
17 | app: gugotik-video-service
18 | branch: master
19 | version: ${BUILD_NUMBER}-${CI_COMMIT_ID}
20 | name: gugotik-video-service
21 | dream-app: gugotik-video-service
22 | dream-unit: gugotik-video-service
23 | spec:
24 | volumes:
25 | - name: volume
26 | persistentVolumeClaim:
27 | claimName: storage
28 | - name: config
29 | configMap:
30 | name: gugotik-log-config
31 | - name: log-volume
32 | emptyDir: { }
33 | imagePullSecrets:
34 | - name: regcred
35 | containers:
36 | - image: ${IMAGE}
37 | imagePullPolicy: IfNotPresent
38 | name: gugotik-video-service
39 | command:
40 | - ./services/videoprocessor/VideoprocessorService
41 | envFrom:
42 | - configMapRef:
43 | name: env-config
44 | - configMapRef:
45 | name: gugotik-env
46 | - secretRef:
47 | name: gugotik-secret
48 | ports:
49 | - name: metrics-37099
50 | containerPort: 37099
51 | protocol: TCP
52 | resources:
53 | limits:
54 | cpu: 4000m
55 | memory: 8Gi
56 | requests:
57 | cpu: 100m
58 | memory: 128Mi
59 | volumeMounts:
60 | - mountPath: /data/apps/gugotik-service-bundle/data
61 | name: volume
62 | - mountPath: /var/log/gugotik
63 | name: log-volume
64 | - name: logger
65 | image: fluent/fluent-bit:1.8.4
66 | imagePullPolicy: IfNotPresent
67 | resources:
68 | requests:
69 | cpu: 20m
70 | memory: 100Mi
71 | limits:
72 | cpu: 100m
73 | memory: 200Mi
74 | volumeMounts:
75 | - mountPath: /fluent-bit/etc
76 | name: config
77 | - mountPath: /var/log/gugotik
78 | name: log-volume
79 | terminationGracePeriodSeconds: 30
--------------------------------------------------------------------------------
/manifests-endymx/sevice-http-api.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app: gugotik-http-service
6 | name: gugotik-http-service
7 | namespace: gugotik-service-bundle
8 | spec:
9 | ports:
10 | - name: http
11 | port: 37000
12 | protocol: TCP
13 | targetPort: 37000
14 | selector:
15 | name: gugotik-http-service
16 | branch: master
17 | type: ClusterIP
--------------------------------------------------------------------------------
/promethus.docker.compose.yml:
--------------------------------------------------------------------------------
1 | # my global config
2 | global:
3 | scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
4 | evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
5 | # scrape_timeout is set to the global default (10s).
6 |
7 | # Alertmanager configuration
8 | alerting:
9 | alertmanagers:
10 | - static_configs:
11 | - targets:
12 | # - alertmanager:9093
13 |
14 | # Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
15 | rule_files:
16 | # - "first_rules.yml"
17 | # - "second_rules.yml"
18 |
19 | # A scrape configuration containing exactly one endpoint to scrape:
20 | # Here it's Prometheus itself.
21 | scrape_configs:
22 | # The job name is added as a label `job=` to any timeseries scraped from this config.
23 | - job_name: "prometheus"
24 | # metrics_path defaults to '/metrics'
25 | # scheme defaults to 'http'.
26 | static_configs:
27 | - targets: [ "localhost:9090" ]
28 | - targets: [ "gateway:37000" ]
29 | - targets: [ "auth:37099" ]
30 | - targets: [ "user:37099" ]
31 | - targets: [ "comment:37099" ]
32 | - targets: [ "favorite:37099" ]
33 | - targets: [ "feed:37099" ]
34 | - targets: [ "message:37099" ]
35 | - targets: [ "publish:37099" ]
36 | - targets: [ "recommend:37099" ]
37 | - targets: [ "relation:37099" ]
--------------------------------------------------------------------------------
/scripts/build-all.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | echo Please Run Me on the root dir, not in scripts dir.
4 |
5 | IF EXIST output (
6 | echo "Output dir existed, deleting and recreating..."
7 | rd /s /q output
8 | )
9 | mkdir output\services
10 | pushd src\services
11 | for /D %%i in (*) do (
12 | if not "%%i"=="health" (
13 | set "name=%%i"
14 | setlocal enabledelayedexpansion
15 | set "capName=!name:~0,1!"
16 | set "capName=!capName:a=A!"
17 | set "capName=!capName:b=B!"
18 | set "capName=!capName:c=C!"
19 | set "capName=!capName:d=D!"
20 | set "capName=!capName:e=E!"
21 | set "capName=!capName:f=F!"
22 | set "capName=!capName:g=G!"
23 | set "capName=!capName:h=H!"
24 | set "capName=!capName:i=I!"
25 | set "capName=!capName:j=J!"
26 | set "capName=!capName:k=K!"
27 | set "capName=!capName:l=L!"
28 | set "capName=!capName:m=M!"
29 | set "capName=!capName:n=N!"
30 | set "capName=!capName:o=O!"
31 | set "capName=!capName:p=P!"
32 | set "capName=!capName:q=Q!"
33 | set "capName=!capName:r=R!"
34 | set "capName=!capName:s=S!"
35 | set "capName=!capName:t=T!"
36 | set "capName=!capName:u=U!"
37 | set "capName=!capName:v=V!"
38 | set "capName=!capName:w=W!"
39 | set "capName=!capName:x=X!"
40 | set "capName=!capName:y=Y!"
41 | set "capName=!capName:z=Z!"
42 | set "capName=!capName!!name:~1!"
43 | cd %%i
44 | go build -o ../../../output/services/%%i/!capName!Service.exe
45 | cd ..
46 | endlocal
47 | )
48 | )
49 |
50 |
51 | popd
52 | mkdir output\gateway
53 |
54 | cd src\web
55 | go build -o ../../output/gateway/Gateway.exe
56 | echo OK!
57 |
--------------------------------------------------------------------------------
/scripts/build-all.ps1:
--------------------------------------------------------------------------------
1 | Write-Host "Please Run Me on the root dir, not in scripts dir."
2 |
3 | if (Test-Path output) {
4 | Write-Host "Output dir existed, deleting and recreating..."
5 | Remove-Item -Recurse -Force output
6 | }
7 | New-Item -ItemType Directory -Path output\services | Out-Null
8 |
9 | Set-Location src\services
10 |
11 | $directories = Get-ChildItem -Directory | Where-Object { $_.Name -ne 'health' }
12 |
13 | foreach ($dir in $directories) {
14 | $capitalizedName = $dir.Name.Substring(0, 1).ToUpper() + $dir.Name.Substring(1)
15 |
16 | Set-Location -Path $dir.FullName
17 | & go build -o "../../../output/services/$($dir.Name)/$($capitalizedName)Service.exe"
18 | Set-Location -Path ".."
19 | }
20 |
21 | Set-Location "..\.."
22 |
23 | New-Item -ItemType Directory -Path output\gateway | Out-Null
24 |
25 | Set-Location src\web
26 | & go build -o "../../output/gateway/GateWay.exe"
27 | Set-Location "..\.."
28 |
29 | Write-Host "OK!"
30 |
--------------------------------------------------------------------------------
/scripts/build-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "Please Run Me on the root dir, not in scripts dir."
4 |
5 | if [ -d "output" ]; then
6 | echo "Output dir existed, deleting and recreating..."
7 | rm -rf output
8 | fi
9 | mkdir -p output/services
10 |
11 | pushd src/services || exit
12 |
13 | for i in *; do
14 | if [ "$i" != "health" ]; then
15 | name="$i"
16 | capName="${name^}"
17 | cd "$i" || exit
18 | go build -o "../../../output/services/$i/${capName}Service"
19 | cd ..
20 | fi
21 | done
22 |
23 | popd || exit
24 |
25 | mkdir -p output/gateway
26 |
27 | cd src/web || exit
28 |
29 | go build -o "../../output/gateway/Gateway"
30 |
31 | echo "OK!"
32 |
--------------------------------------------------------------------------------
/scripts/run-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "Please Run Me on the root dir, not in scripts dir."
4 |
5 | gateway_directory="output/gateway"
6 | service_directory="output/services"
7 |
8 | log_directory="log"
9 |
10 | mkdir -p "$log_directory"
11 |
12 | if [ ! -d "output" ]; then
13 | echo "Output dir does not exist, please run build script first."
14 | fi
15 |
16 | for gateway_file in "$gateway_directory"/*; do
17 | if [[ -x "$gateway_file" && -f "$gateway_file" ]]; then
18 | echo "Running $gateway_file"
19 | gateway_log_file="$log_directory"/"$(basename gateway_file)".log
20 | ./"$gateway_file" >> "$gateway_log_file" 2>&1 &
21 | fi
22 | done
23 |
24 | for service in "$service_directory"/*; do
25 | for service_file in "$service"/*; do
26 | if [[ -x "$service_file" && -f "$service_file" ]]; then
27 | echo "Running $service_file"
28 | service_log_file="$log_directory"/"$(basename service_file)".log
29 | ./"$service_file" >> "$service_log_file" 2>&1 &
30 | fi
31 | done
32 | done
33 |
--------------------------------------------------------------------------------
/src/constant/config/env.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/caarlos0/env/v6"
5 | "github.com/joho/godotenv"
6 | log "github.com/sirupsen/logrus"
7 | )
8 |
9 | var EnvCfg envConfig
10 |
11 | type envConfig struct {
12 | ConsulAddr string `env:"CONSUL_ADDR" envDefault:"localhost:8500"`
13 | LoggerLevel string `env:"LOGGER_LEVEL" envDefault:"INFO"`
14 | LoggerWithTraceState string `env:"LOGGER_OUT_TRACING" envDefault:"disable"`
15 | TiedLogging string `env:"TIED" envDefault:"NONE"`
16 | PostgreSQLHost string `env:"POSTGRESQL_HOST"`
17 | PostgreSQLPort string `env:"POSTGRESQL_PORT"`
18 | PostgreSQLUser string `env:"POSTGRESQL_USER"`
19 | PostgreSQLPassword string `env:"POSTGRESQL_PASSWORD"`
20 | PostgreSQLDataBase string `env:"POSTGRESQL_DATABASE"`
21 | StorageType string `env:"STORAGE_TYPE" envDefault:"fs"`
22 | FileSystemStartPath string `env:"FS_PATH" envDefault:"/tmp"`
23 | FileSystemBaseUrl string `env:"FS_BASEURL" envDefault:"http://localhost/"`
24 | RedisAddr string `env:"REDIS_ADDR"`
25 | RedisPassword string `env:"REDIS_PASSWORD" envDefault:""`
26 | RedisDB int `env:"REDIS_DB" envDefault:"0"`
27 | TracingEndPoint string `env:"TRACING_ENDPOINT"`
28 | PyroscopeState string `env:"PYROSCOPE_STATE" envDefault:"false"`
29 | PyroscopeAddr string `env:"PYROSCOPE_ADDR"`
30 | RedisPrefix string `env:"REDIS_PREFIX" envDefault:"GUGUTIK"`
31 | PostgreSQLSchema string `env:"POSTGRESQL_SCHEMA" envDefault:""`
32 | RedisMaster string `env:"REDIS_MASTER"`
33 | ConsulAnonymityPrefix string `env:"CONSUL_ANONYMITY_NAME" envDefault:""`
34 | RabbitMQUsername string `env:"RABBITMQ_USERNAME" envDefault:"guest"`
35 | RabbitMQPassword string `env:"RABBITMQ_PASSWORD" envDefault:"guest"`
36 | RabbitMQAddr string `env:"RABBITMQ_ADDRESS" envDefault:"localhost"`
37 | RabbitMQPort string `env:"RABBITMQ_PORT" envDefault:"5672"`
38 | RabbitMQVhostPrefix string `env:"RABBITMQ_VHOST_PREFIX" envDefault:""`
39 | ChatGPTAPIKEYS string `env:"CHATGPT_API_KEYS"`
40 | PodIpAddr string `env:"POD_IP" envDefault:"localhost"`
41 | GorseAddr string `env:"GORSE_ADDR"`
42 | GorseApiKey string `env:"GORSE_APIKEY"`
43 | MagicUserId uint32 `env:"MAGIC_USER_ID" envDefault:"1"`
44 | ChatGptProxy string `env:"CHATGPT_PROXY"`
45 | PostgreSQLPrefix string `env:"POSTGRESQL_PREFIX" envDefault:""`
46 | PostgreSQLReplicaState string `env:"POSTGRESQL_REPLICA"`
47 | PostgreSQLReplicaAddress string `env:"POSTGRESQL_REPLICA_ADDR"`
48 | PostgreSQLReplicaUsername string `env:"POSTGRESQL_REPLICA_USER"`
49 | PostgreSQLReplicaPassword string `env:"POSTGRESQL_REPLICA_PASSWORD"`
50 | OtelState string `env:"TRACING_STATE" envDefault:"enable"`
51 | OtelSampler float64 `env:"TRACING_SAMPLER" envDefault:"0.01"`
52 | AnonymityUser string `env:"ANONYMITY_USER" envDefault:"114514"`
53 | ElasticsearchUrl string `env:"ES_ADDR"`
54 | }
55 |
56 | func init() {
57 | if err := godotenv.Load(); err != nil {
58 | log.Errorf("Can not read env from file system, please check the right this program owned.")
59 | }
60 |
61 | EnvCfg = envConfig{}
62 |
63 | if err := env.Parse(&EnvCfg); err != nil {
64 | panic("Can not parse env from file system, please check the env.")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/constant/config/service.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | const WebServiceName = "GuGoTik-GateWay"
4 | const WebServiceAddr = ":37000"
5 |
6 | const AuthRpcServerName = "GuGoTik-AuthService"
7 | const AuthRpcServerPort = ":37001"
8 |
9 | const UserRpcServerName = "GuGoTik-UserService"
10 | const UserRpcServerPort = ":37002"
11 |
12 | const CommentRpcServerName = "GuGoTik-CommentService"
13 | const CommentRpcServerPort = ":37003"
14 |
15 | const FeedRpcServerName = "GuGoTik-FeedService"
16 | const FeedRpcServerPort = ":37004"
17 |
18 | const PublishRpcServerName = "GuGoTik-PublisherService"
19 | const PublishRpcServerPort = ":37005"
20 |
21 | const FavoriteRpcServerName = "GuGoTik-FavoriteService"
22 | const FavoriteRpcServerPort = ":37006"
23 |
24 | const MessageRpcServerName = "GuGoTik-MessageService"
25 | const MessageRpcServerPort = ":37007"
26 |
27 | const RelationRpcServerName = "GuGoTik-RelationService"
28 | const RelationRpcServerPort = ":37008"
29 |
30 | const RecommendRpcServiceName = "GuGoTik-Recommend"
31 | const RecommendRpcServicePort = ":37009"
32 |
33 | const Metrics = ":37099"
34 | const VideoProcessorRpcServiceName = "GuGoTik-VideoProcessorService"
35 |
36 | const VideoPicker = "GuGoTik-VideoPicker"
37 | const Event = "GuGoTik-Recommend"
38 | const MsgConsumer = "GuGoTik-MgsConsumer"
39 |
40 | const BloomRedisChannel = "GuGoTik-Bloom"
41 |
42 | const MaxVideoSize = 200 * 1024 * 1024
43 |
--------------------------------------------------------------------------------
/src/constant/strings/common.go:
--------------------------------------------------------------------------------
1 | package strings
2 |
3 | const (
4 | ServiceOKCode = 0
5 | ServiceOK = "success"
6 | )
7 |
--------------------------------------------------------------------------------
/src/constant/strings/err.go:
--------------------------------------------------------------------------------
1 | package strings
2 |
3 | // Bad Request
4 | const (
5 | GateWayErrorCode = 40001
6 | GateWayError = "GuGoTik Gateway 暂时不能处理您的请求,请稍后重试!"
7 | GateWayParamsErrorCode = 40002
8 | GateWayParamsError = "GuGoTik Gateway 无法响应您的请求,请重启 APP 或稍后再试!"
9 | )
10 |
11 | // Server Inner Error
12 | const (
13 | AuthServiceInnerErrorCode = 50001
14 | AuthServiceInnerError = "登录服务出现内部错误,请稍后重试!"
15 | VideoServiceInnerErrorCode = 50002
16 | VideoServiceInnerError = "视频发布服务出现内部错误,请稍后重试!"
17 | UnableToQueryUserErrorCode = 50003
18 | UnableToQueryUserError = "无法查询到对应用户"
19 | UnableToQueryCommentErrorCode = 50004
20 | UnableToQueryCommentError = "无法查询到视频评论"
21 | UnableToCreateCommentErrorCode = 50005
22 | UnableToCreateCommentError = "无法创建评论"
23 | FeedServiceInnerErrorCode = 50006
24 | FeedServiceInnerError = "视频服务出现内部错误,请稍后重试!"
25 | ActorIDNotMatchErrorCode = 50007
26 | ActorIDNotMatchError = "用户不匹配"
27 | UnableToDeleteCommentErrorCode = 50008
28 | UnableToDeleteCommentError = "无法删除视频评论"
29 | UnableToAddMessageErrorCode = 50009
30 | UnableToAddMessageError = "发送消息出错"
31 | UnableToQueryMessageErrorCode = 50010
32 | UnableToQueryMessageError = "查消息出错"
33 | PublishServiceInnerErrorCode = 50011
34 | PublishServiceInnerError = "发布服务出现内部错误,请稍后重试!"
35 | UnableToFollowErrorCode = 50012
36 | UnableToFollowError = "关注该用户失败"
37 | UnableToUnFollowErrorCode = 50013
38 | UnableToUnFollowError = "取消关注失败"
39 | UnableToGetFollowListErrorCode = 50014
40 | UnableToGetFollowListError = "无法查询到关注列表"
41 | UnableToGetFollowerListErrorCode = 50015
42 | UnableToGetFollowerListError = "无法查询到粉丝列表"
43 | UnableToRelateYourselfErrorCode = 50016
44 | UnableToRelateYourselfError = "无法关注自己"
45 | RelationNotFoundErrorCode = 50017
46 | RelationNotFoundError = "未关注该用户"
47 | StringToIntErrorCode = 50018
48 | StringToIntError = "字符串转数字失败"
49 | RelationServiceIntErrorCode = 50019
50 | RelationServiceIntError = "关系服务出现内部错误"
51 | FavoriteServiceErrorCode = 50020
52 | FavoriteServiceError = "点赞服务内部出错"
53 | UserServiceInnerErrorCode = 50021
54 | UserServiceInnerError = "登录服务出现内部错误,请稍后重试!"
55 | UnableToQueryVideoErrorCode = 50022
56 | UnableToQueryVideoError = "无法查询到该视频"
57 | AlreadyFollowingErrorCode = 50023
58 | AlreadyFollowingError = "无法关注已关注的人"
59 | UnableToGetFriendListErrorCode = 50024
60 | UnableToGetFriendListError = "无法查询到好友列表"
61 | RecommendServiceInnerErrorCode = 50025
62 | RecommendServiceInnerError = "推荐系统内部错误"
63 | )
64 |
65 | // Expected Error
66 | const (
67 | AuthUserExistedCode = 10001
68 | AuthUserExisted = "用户已存在,请更换用户名或尝试登录!"
69 | UserNotExistedCode = 10002
70 | UserNotExisted = "用户不存在,请先注册或检查你的用户名是否正确!"
71 | AuthUserLoginFailedCode = 10003
72 | AuthUserLoginFailed = "用户信息错误,请检查账号密码是否正确"
73 | AuthUserNeededCode = 10004
74 | AuthUserNeeded = "用户无权限操作,请登陆后重试!"
75 | ActionCommentTypeInvalidCode = 10005
76 | ActionCommentTypeInvalid = "不合法的评论类型"
77 | ActionCommentLimitedCode = 10006
78 | ActionCommentLimited = "评论频繁,请稍后再试!"
79 | InvalidContentTypeCode = 10007
80 | InvalidContentType = "不合法的内容类型"
81 | FavoriteServiceDuplicateCode = 10008
82 | FavoriteServiceDuplicateError = "不能重复点赞"
83 | FavoriteServiceCancelCode = 10009
84 | FavoriteServiceCancelError = "没有点赞,不能取消点赞"
85 | PublishVideoLimitedCode = 10010
86 | PublishVideoLimited = "视频发布频繁,请稍后再试!"
87 | ChatActionLimitedCode = 10011
88 | ChatActionLimitedError = "发送消息频繁,请稍后再试!"
89 | FollowLimitedCode = 10012
90 | FollowLimited = "关注频繁,请稍后再试!"
91 | UserDoNotExistedCode = 10013
92 | UserDoNotExisted = "查询用户不存在!"
93 | OversizeVideoCode = 10014
94 | OversizeVideo = "上传视频超过了200MB"
95 | )
96 |
--------------------------------------------------------------------------------
/src/constant/strings/service.go:
--------------------------------------------------------------------------------
1 | package strings
2 |
3 | // Exchange name
4 | const (
5 | VideoExchange = "video_exchange"
6 | EventExchange = "event"
7 | MessageExchange = "message_exchange"
8 | AuditExchange = "audit_exchange"
9 | )
10 |
11 | // Queue name
12 | const (
13 | VideoPicker = "video_picker"
14 | VideoSummary = "video_summary"
15 | MessageCommon = "message_common"
16 | MessageGPT = "message_gpt"
17 | MessageES = "message_es"
18 | AuditPicker = "audit_picker"
19 | )
20 |
21 | // Routing key
22 | const (
23 | FavoriteActionEvent = "video.favorite.action"
24 | VideoGetEvent = "video.get.action"
25 | VideoCommentEvent = "video.comment.action"
26 | VideoPublishEvent = "video.publish.action"
27 |
28 | MessageActionEvent = "message.common"
29 | MessageGptActionEvent = "message.gpt"
30 | AuditPublishEvent = "audit"
31 | )
32 |
33 | // Action Type
34 | const (
35 | FavoriteIdActionLog = 1 // 用户点赞相关操作
36 | FollowIdActionLog = 2 // 用户关注相关操作
37 | )
38 |
39 | // Action Name
40 | const (
41 | FavoriteNameActionLog = "favorite.action" // 用户点赞操作名称
42 | FavoriteUpActionSubLog = "up"
43 | FavoriteDownActionSubLog = "down"
44 |
45 | FollowNameActionLog = "follow.action" // 用户关注操作名称
46 | FollowUpActionSubLog = "up"
47 | FollowDownActionSubLog = "down"
48 | )
49 |
50 | // Action Service Name
51 | const (
52 | FavoriteServiceName = "FavoriteService"
53 | FollowServiceName = "FollowService"
54 | )
55 |
--------------------------------------------------------------------------------
/src/extra/gorse/model.go:
--------------------------------------------------------------------------------
1 | package gorse
2 |
3 | import "time"
4 |
5 | type Feedback struct {
6 | FeedbackType string `json:"FeedbackType"`
7 | UserId string `json:"UserId"`
8 | ItemId string `json:"ItemId"`
9 | Timestamp string `json:"Timestamp"`
10 | }
11 |
12 | type Feedbacks struct {
13 | Cursor string `json:"Cursor"`
14 | Feedback []Feedback `json:"Feedback"`
15 | }
16 |
17 | type ErrorMessage string
18 |
19 | func (e ErrorMessage) Error() string {
20 | return string(e)
21 | }
22 |
23 | type RowAffected struct {
24 | RowAffected int `json:"RowAffected"`
25 | }
26 |
27 | type Score struct {
28 | Id string `json:"Id"`
29 | Score float64 `json:"Score"`
30 | }
31 |
32 | type User struct {
33 | UserId string `json:"UserId"`
34 | Labels []string `json:"Labels"`
35 | Subscribe []string `json:"Subscribe"`
36 | Comment string `json:"Comment"`
37 | }
38 |
39 | type Users struct {
40 | Cursor string `json:"Cursor"`
41 | Users []User `json:"Users"`
42 | }
43 |
44 | type UserPatch struct {
45 | Labels []string
46 | Subscribe []string
47 | Comment *string
48 | }
49 |
50 | type Item struct {
51 | ItemId string `json:"ItemId"`
52 | IsHidden bool `json:"IsHidden"`
53 | Labels []string `json:"Labels"`
54 | Categories []string `json:"Categories"`
55 | Timestamp string `json:"Timestamp"`
56 | Comment string `json:"Comment"`
57 | }
58 |
59 | type Items struct {
60 | Cursor string `json:"Cursor"`
61 | Items []Item `json:"Items"`
62 | }
63 |
64 | type ItemPatch struct {
65 | IsHidden *bool
66 | Categories []string
67 | Timestamp *time.Time
68 | Labels []string
69 | Comment *string
70 | }
71 |
72 | type Health struct {
73 | CacheStoreConnected bool `json:"CacheStoreConnected"`
74 | CacheStoreError string `json:"CacheStoreError"`
75 | DataStoreConnected bool `json:"DataStoreConnected"`
76 | DataStoreError string `json:"DataStoreError"`
77 | Ready bool `json:"Ready"`
78 | }
79 |
--------------------------------------------------------------------------------
/src/extra/profiling/analyzer.go:
--------------------------------------------------------------------------------
1 | package profiling
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "github.com/pyroscope-io/client/pyroscope"
7 | log "github.com/sirupsen/logrus"
8 | "gorm.io/plugin/opentelemetry/logging/logrus"
9 | "os"
10 | "runtime"
11 | )
12 |
13 | func InitPyroscope(appName string) {
14 | if config.EnvCfg.PyroscopeState != "enable" {
15 | logging.Logger.WithFields(log.Fields{
16 | "appName": appName,
17 | }).Infof("User close Pyroscope, the service would not run.")
18 | return
19 | }
20 |
21 | runtime.SetMutexProfileFraction(5)
22 | runtime.SetBlockProfileRate(5)
23 |
24 | _, err := pyroscope.Start(pyroscope.Config{
25 | ApplicationName: appName,
26 | ServerAddress: config.EnvCfg.PyroscopeAddr,
27 | Logger: logrus.NewWriter(),
28 | Tags: map[string]string{"hostname": os.Getenv("HOSTNAME")},
29 | ProfileTypes: []pyroscope.ProfileType{
30 | pyroscope.ProfileCPU,
31 | pyroscope.ProfileAllocObjects,
32 | pyroscope.ProfileAllocSpace,
33 | pyroscope.ProfileInuseObjects,
34 | pyroscope.ProfileInuseSpace,
35 | pyroscope.ProfileGoroutines,
36 | pyroscope.ProfileMutexCount,
37 | pyroscope.ProfileMutexDuration,
38 | pyroscope.ProfileBlockCount,
39 | pyroscope.ProfileBlockDuration,
40 | },
41 | })
42 |
43 | if err != nil {
44 | logging.Logger.WithFields(log.Fields{
45 | "appName": appName,
46 | "err": err,
47 | }).Warnf("Pyroscope failed to run.")
48 | return
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/extra/tracing/otel.go:
--------------------------------------------------------------------------------
1 | package tracing
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "context"
7 | "github.com/sirupsen/logrus"
8 | "go.opentelemetry.io/otel"
9 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace"
10 | "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
11 | "go.opentelemetry.io/otel/propagation"
12 | "go.opentelemetry.io/otel/sdk/resource"
13 | "go.opentelemetry.io/otel/sdk/trace"
14 | semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
15 | trace2 "go.opentelemetry.io/otel/trace"
16 | )
17 |
18 | var Tracer trace2.Tracer
19 |
20 | func SetTraceProvider(name string) (*trace.TracerProvider, error) {
21 | client := otlptracehttp.NewClient(
22 | otlptracehttp.WithEndpoint(config.EnvCfg.TracingEndPoint),
23 | otlptracehttp.WithInsecure(),
24 | )
25 | exporter, err := otlptrace.New(context.Background(), client)
26 | if err != nil {
27 | logging.Logger.WithFields(logrus.Fields{
28 | "err": err,
29 | }).Errorf("Can not init otel !")
30 | return nil, err
31 | }
32 |
33 | var sampler trace.Sampler
34 | if config.EnvCfg.OtelState == "disable" {
35 | sampler = trace.NeverSample()
36 | } else {
37 | sampler = trace.TraceIDRatioBased(config.EnvCfg.OtelSampler)
38 | }
39 |
40 | tp := trace.NewTracerProvider(
41 | trace.WithBatcher(exporter),
42 | trace.WithResource(
43 | resource.NewWithAttributes(
44 | semconv.SchemaURL,
45 | semconv.ServiceNameKey.String(name),
46 | ),
47 | ),
48 | trace.WithSampler(sampler),
49 | )
50 | otel.SetTracerProvider(tp)
51 | otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
52 | Tracer = otel.Tracer(name)
53 | return tp, nil
54 | }
55 |
--------------------------------------------------------------------------------
/src/idl/auth.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package rpc.auth;
3 | option go_package = "GuGoTik/src/rpc/auth";
4 |
5 | message LoginRequest {
6 | string username = 1; // 登录用户名
7 | string password = 2; // 登录密码
8 | }
9 |
10 | message LoginResponse {
11 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
12 | string status_msg = 2; // 返回状态描述
13 | uint32 user_id = 3; // 用户id
14 | string token = 4; // 用户鉴权token
15 | }
16 |
17 | message RegisterRequest {
18 | string username = 1; // 注册用户名,最长32个字符
19 | string password = 2; // 密码,最长32个字符
20 | }
21 |
22 | message RegisterResponse {
23 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
24 | string status_msg = 2; // 返回状态描述
25 | uint32 user_id = 3; // 用户id
26 | string token = 4; // 用户鉴权token
27 | }
28 |
29 | message AuthenticateRequest {
30 | string token = 1; // 用户鉴权token
31 | }
32 |
33 | message AuthenticateResponse {
34 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
35 | string status_msg = 2; // 返回状态描述
36 | uint32 user_id = 3; // 用户id
37 | }
38 |
39 | service AuthService {
40 | rpc Authenticate (AuthenticateRequest) returns (AuthenticateResponse);
41 |
42 | rpc Register (RegisterRequest) returns (RegisterResponse);
43 |
44 | rpc Login (LoginRequest) returns (LoginResponse);
45 | }
--------------------------------------------------------------------------------
/src/idl/chat.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package rpc.chat;
3 | option go_package = "GuGoTik/src/rpc/chat";
4 |
5 | message ChatRequest {
6 | uint32 actor_id = 1;
7 | uint32 user_id = 2;
8 | uint64 pre_msg_time = 3;
9 | }
10 |
11 | message Message {
12 | uint32 id = 1;
13 | string content = 2;
14 | uint64 create_time = 3;
15 | optional uint32 from_user_id = 4;
16 | optional uint32 to_user_id = 5;
17 | }
18 |
19 | message ChatResponse {
20 | int32 status_code = 1;
21 | string status_msg = 2;
22 | repeated Message message_list = 3;
23 | }
24 |
25 | message ActionRequest {
26 | uint32 actor_id = 1;
27 | uint32 user_id = 2;
28 | uint32 action_type = 3; // 1-发送消息
29 | string content = 4;
30 | }
31 |
32 | message ActionResponse {
33 | int32 status_code = 1;
34 | string status_msg = 2;
35 | }
36 |
37 | service ChatService {
38 | rpc Chat(ChatRequest) returns (ChatResponse);
39 |
40 | rpc ChatAction(ActionRequest) returns (ActionResponse);
41 | }
42 |
--------------------------------------------------------------------------------
/src/idl/check.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package grpc.health.v1;
4 | option go_package = "GuGoTik/src/rpc/health";
5 | message HealthCheckRequest {
6 | string service = 1;
7 | }
8 |
9 | message HealthCheckResponse {
10 | enum ServingStatus {
11 | UNKNOWN = 0;
12 | SERVING = 1;
13 | NOT_SERVING = 2;
14 | SERVICE_UNKNOWN = 3; // Used only by the Watch method.
15 | }
16 | ServingStatus status = 1;
17 | }
18 |
19 | service Health {
20 | rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
21 |
22 | rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
23 | }
--------------------------------------------------------------------------------
/src/idl/comment.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | import "user.proto";
3 | package rpc.comment;
4 | option go_package = "GuGoTik/src/rpc/comment";
5 |
6 | message Comment {
7 | uint32 id = 1;
8 | user.User user = 2;
9 | string content = 3;
10 | string create_date = 4;
11 | }
12 |
13 | enum ActionCommentType {
14 | ACTION_COMMENT_TYPE_UNSPECIFIED = 0;
15 | ACTION_COMMENT_TYPE_ADD = 1;
16 | ACTION_COMMENT_TYPE_DELETE = 2;
17 | }
18 |
19 | message ActionCommentRequest {
20 | uint32 actor_id = 1;
21 | uint32 video_id = 2;
22 | ActionCommentType action_type = 3;
23 | oneof action {
24 | string comment_text = 4;
25 | uint32 comment_id = 5;
26 | }
27 | }
28 |
29 | message ActionCommentResponse {
30 | int32 status_code = 1;
31 | string status_msg = 2;
32 | optional Comment comment = 3;
33 | }
34 |
35 | message ListCommentRequest {
36 | uint32 actor_id = 1;
37 | uint32 video_id = 2;
38 | }
39 |
40 | message ListCommentResponse {
41 | int32 status_code = 1;
42 | string status_msg = 2;
43 | repeated Comment comment_list = 3;
44 | }
45 |
46 | message CountCommentRequest {
47 | uint32 actor_id = 1;
48 | uint32 video_id = 2;
49 | }
50 |
51 | message CountCommentResponse {
52 | int32 status_code = 1;
53 | string status_msg = 2;
54 | uint32 comment_count = 3;
55 | }
56 |
57 | service CommentService {
58 | rpc ActionComment(ActionCommentRequest) returns (ActionCommentResponse);
59 | rpc ListComment(ListCommentRequest) returns (ListCommentResponse);
60 | rpc CountComment(CountCommentRequest) returns (CountCommentResponse);
61 | }
--------------------------------------------------------------------------------
/src/idl/favorite.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | import "feed.proto";
3 | package rpc.favorite;
4 | option go_package = "GuGoTik/src/rpc/favorite";
5 |
6 | message FavoriteRequest {
7 | uint32 actor_id = 1; // 用户id
8 | uint32 video_id = 2; // 视频id
9 | uint32 action_type = 3; // 1-点赞,2-取消点赞
10 | }
11 |
12 | message FavoriteResponse {
13 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
14 | string status_msg = 2; // 返回状态描述
15 | }
16 |
17 | message FavoriteListRequest {
18 | uint32 actor_id = 1; // 发出请求的用户的id
19 | uint32 user_id = 2; // 用户id
20 | }
21 |
22 | message FavoriteListResponse {
23 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
24 | string status_msg = 2; // 返回状态描述
25 | repeated feed.Video video_list = 3; // 用户点赞视频列表
26 | }
27 |
28 | message IsFavoriteRequest {
29 | uint32 actor_id = 1; // 用户id
30 | uint32 video_id = 2; // 视频id
31 | }
32 |
33 | message IsFavoriteResponse {
34 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
35 | string status_msg = 2; // 返回状态描述
36 | bool result = 3; // 结果
37 | }
38 |
39 | message CountFavoriteRequest {
40 | uint32 video_id = 1; // 视频id
41 | }
42 |
43 | message CountFavoriteResponse {
44 | int32 status_code = 1;
45 | string status_msg = 2;
46 | uint32 count = 3; // 点赞数
47 | }
48 |
49 | message CountUserFavoriteRequest {
50 | uint32 user_id = 1; // 用户id
51 | }
52 |
53 | message CountUserFavoriteResponse {
54 | int32 status_code = 1;
55 | string status_msg = 2;
56 | uint32 count = 3; // 点赞数
57 | }
58 |
59 | message CountUserTotalFavoritedRequest {
60 | uint32 actor_id = 1;
61 | uint32 user_id = 2;
62 | }
63 |
64 | message CountUserTotalFavoritedResponse {
65 | int32 status_code = 1;
66 | string status_msg = 2;
67 | uint32 count = 3; // 点赞数
68 | }
69 |
70 | service FavoriteService {
71 | rpc FavoriteAction (FavoriteRequest) returns (FavoriteResponse);
72 |
73 | rpc FavoriteList (FavoriteListRequest) returns (FavoriteListResponse);
74 |
75 | rpc IsFavorite (IsFavoriteRequest) returns (IsFavoriteResponse);
76 |
77 | rpc CountFavorite (CountFavoriteRequest) returns (CountFavoriteResponse);
78 |
79 | rpc CountUserFavorite (CountUserFavoriteRequest) returns (CountUserFavoriteResponse);
80 |
81 | rpc CountUserTotalFavorited (CountUserTotalFavoritedRequest) returns (CountUserTotalFavoritedResponse);
82 | }
83 |
--------------------------------------------------------------------------------
/src/idl/feed.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | import "user.proto";
3 | package rpc.feed;
4 | option go_package = "GuGoTik/src/rpc/feed";
5 |
6 | message Video {
7 | uint32 id = 1;
8 | user.User author = 2;
9 | string play_url = 3;
10 | string cover_url = 4;
11 | uint32 favorite_count = 5;
12 | uint32 comment_count = 6;
13 | bool is_favorite = 7;
14 | string title = 8;
15 | }
16 |
17 | message ListFeedRequest {
18 | optional string latest_time = 1;
19 | optional uint32 actor_id = 2;
20 | }
21 |
22 | message ListFeedResponse {
23 | int32 status_code = 1;
24 | string status_msg = 2;
25 | optional uint64 next_time = 3;
26 | repeated Video video_list = 4;
27 | }
28 |
29 | message QueryVideosRequest {
30 | uint32 actor_id = 1;
31 | repeated uint32 video_ids = 2;
32 | }
33 |
34 | message QueryVideosResponse {
35 | int32 status_code = 1;
36 | string status_msg = 2;
37 | repeated Video video_list = 3;
38 | }
39 |
40 | message VideoExistRequest {
41 | uint32 video_id = 1; // 视频id
42 | }
43 |
44 | message VideoExistResponse {
45 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
46 | string status_msg = 2; // 返回状态描述
47 | bool existed = 3;
48 | }
49 |
50 | message QueryVideoSummaryAndKeywordsRequest {
51 | uint32 actor_id = 1;
52 | uint32 video_id = 2;
53 | }
54 |
55 | message QueryVideoSummaryAndKeywordsResponse {
56 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
57 | string status_msg = 2; // 返回状态描述
58 | string summary = 3;
59 | string keywords = 4;
60 | }
61 |
62 | service FeedService {
63 | rpc ListVideosByRecommend(ListFeedRequest) returns (ListFeedResponse);
64 | rpc ListVideos(ListFeedRequest) returns (ListFeedResponse);
65 | rpc QueryVideos(QueryVideosRequest) returns (QueryVideosResponse);
66 | rpc QueryVideoExisted(VideoExistRequest) returns (VideoExistResponse);
67 | rpc QueryVideoSummaryAndKeywords(QueryVideoSummaryAndKeywordsRequest) returns (QueryVideoSummaryAndKeywordsResponse);
68 | }
69 |
--------------------------------------------------------------------------------
/src/idl/publish.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | import "feed.proto";
3 | package rpc.publish;
4 | option go_package = "GuGoTik/src/rpc/publish";
5 |
6 | message CreateVideoRequest {
7 | uint32 actor_id = 1; // 用户id
8 | bytes data = 2; // 视频数据
9 | string title = 3; // 视频标题
10 | }
11 |
12 | message CreateVideoResponse {
13 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
14 | string status_msg = 2; // 返回状态描述
15 | }
16 |
17 | message ListVideoRequest{
18 | uint32 actor_id = 1; // 用户id
19 | uint32 user_id = 2; // 被请求查询的用户id
20 | }
21 |
22 | message ListVideoResponse{
23 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
24 | string status_msg = 2; // 返回状态描述
25 | repeated feed.Video video_list = 3; // 视频列表
26 | }
27 |
28 | message CountVideoRequest{
29 | uint32 user_id = 1; // 用户id
30 | }
31 |
32 | message CountVideoResponse{
33 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
34 | string status_msg = 2; // 返回状态描述
35 | uint32 count = 3; // 视频数量
36 | }
37 |
38 | service PublishService {
39 | rpc CreateVideo(CreateVideoRequest) returns (CreateVideoResponse) {}
40 | rpc ListVideo(ListVideoRequest) returns (ListVideoResponse) {}
41 | rpc CountVideo(CountVideoRequest) returns (CountVideoResponse) {}
42 | }
43 |
--------------------------------------------------------------------------------
/src/idl/recommand.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package rpc.recommend;
3 | option go_package = "GuGoTik/src/rpc/recommend";
4 |
5 | message RecommendRequest {
6 | uint32 user_id = 1;
7 | int32 offset = 2;// 用户id
8 | int32 number = 3;
9 | }
10 |
11 | message RecommendResponse {
12 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
13 | string status_msg = 2; // 返回状态描述
14 | repeated uint32 video_list = 3; // 视频 Id 列表
15 | }
16 |
17 | message RecommendRegisterRequest {
18 | uint32 user_id = 1; // 用户id
19 | string username = 2;
20 | }
21 |
22 | message RecommendRegisterResponse {
23 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
24 | string status_msg = 2; // 返回状态描述
25 | }
26 |
27 | service RecommendService {
28 | rpc GetRecommendInformation(RecommendRequest) returns (RecommendResponse) {}
29 | rpc RegisterRecommendUser(RecommendRegisterRequest) returns (RecommendRegisterResponse) {}
30 | }
--------------------------------------------------------------------------------
/src/idl/relation.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | import "user.proto";
3 | package rpc.Relation;
4 | option go_package = "GuGoTik/src/rpc/relation";
5 |
6 |
7 | message RelationActionRequest {
8 | uint32 actor_id = 1; // 当前登录用户
9 | uint32 user_id = 2; // 对方用户id
10 | }
11 |
12 | message RelationActionResponse {
13 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
14 | string status_msg = 2; // 返回状态描述
15 | }
16 |
17 | message FollowListRequest {
18 | uint32 actor_id = 1; // 当前登录用户id
19 | uint32 user_id = 2; // 对方用户id
20 | }
21 |
22 | message FollowListResponse {
23 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
24 | string status_msg = 2; // 返回状态描述
25 | repeated user.User user_list = 3; // 用户信息列表
26 | }
27 |
28 | message CountFollowListRequest {
29 | uint32 user_id = 1; // 用户id
30 | }
31 |
32 | message CountFollowListResponse {
33 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
34 | string status_msg = 2; // 返回状态描述
35 | uint32 count = 3; // 关注数
36 | }
37 |
38 | message FollowerListRequest {
39 | uint32 actor_id = 1; // 当前登录用户id
40 | uint32 user_id = 2; // 对方用户id
41 | }
42 |
43 | message FollowerListResponse {
44 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
45 | string status_msg = 2; // 返回状态描述
46 | repeated user.User user_list = 3; // 用户列表
47 | }
48 |
49 | message CountFollowerListRequest {
50 | uint32 user_id = 1; // 用户id
51 | }
52 |
53 | message CountFollowerListResponse {
54 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
55 | string status_msg = 2; // 返回状态描述
56 | uint32 count = 3; // 粉丝数
57 | }
58 |
59 | message FriendListRequest {
60 | uint32 actor_id = 1; // 当前登录用户id
61 | uint32 user_id = 2; // 对方用户id
62 | }
63 |
64 | message FriendListResponse {
65 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
66 | string status_msg = 2; // 返回状态描述
67 | repeated user.User user_list = 3; // 用户列表
68 | }
69 |
70 | message IsFollowRequest {
71 | uint32 actor_id = 1;
72 | uint32 user_id = 2;
73 | }
74 |
75 | message IsFollowResponse {
76 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
77 | string status_msg = 2; // 返回状态描述
78 | bool result = 3; // 结果
79 | }
80 |
81 | service RelationService {
82 | rpc Follow (RelationActionRequest) returns (RelationActionResponse);
83 |
84 | rpc Unfollow (RelationActionRequest) returns (RelationActionResponse);
85 |
86 | rpc GetFollowList (FollowListRequest) returns (FollowListResponse);
87 |
88 | rpc CountFollowList (CountFollowListRequest) returns (CountFollowListResponse);
89 |
90 | rpc GetFollowerList (FollowerListRequest) returns (FollowerListResponse);
91 |
92 | rpc CountFollowerList (CountFollowerListRequest) returns (CountFollowerListResponse);
93 |
94 | rpc GetFriendList (FriendListRequest) returns (FriendListResponse);
95 |
96 | rpc IsFollow (IsFollowRequest) returns (IsFollowResponse);
97 | }
98 |
--------------------------------------------------------------------------------
/src/idl/user.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package rpc.user;
3 | option go_package = "GuGoTik/src/rpc/user";
4 |
5 | message UserRequest {
6 | uint32 user_id = 1; // 用户id
7 | uint32 actor_id = 2; // 发送请求的用户的id
8 | }
9 |
10 | message UserResponse {
11 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
12 | string status_msg = 2; // 返回状态描述
13 | User user = 3; // 用户信息
14 | }
15 |
16 | message UserExistRequest {
17 | uint32 user_id = 1; // 用户id
18 | }
19 |
20 | message UserExistResponse {
21 | int32 status_code = 1; // 状态码,0-成功,其他值-失败
22 | string status_msg = 2; // 返回状态描述
23 | bool existed = 3;
24 | }
25 |
26 | message User {
27 | uint32 id = 1; // 用户id
28 | string name = 2; // 用户名称
29 | optional uint32 follow_count = 3; // 关注总数
30 | optional uint32 follower_count = 4; // 粉丝总数
31 | bool is_follow = 5; // true-已关注,false-未关注
32 | optional string avatar = 6; //用户头像
33 | optional string background_image = 7; //用户个人页顶部大图
34 | optional string signature = 8; //个人简介
35 | optional uint32 total_favorited = 9; //获赞数量
36 | optional uint32 work_count = 10; //作品数量
37 | optional uint32 favorite_count = 11; //点赞数量
38 | }
39 |
40 | service UserService{
41 | rpc GetUserInfo(UserRequest) returns(UserResponse);
42 |
43 | rpc GetUserExistInformation(UserExistRequest) returns(UserExistResponse);
44 | }
--------------------------------------------------------------------------------
/src/models/action.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type Action struct {
9 | Type uint // 用户操作的行为类型,如:1表示点赞相关
10 | Name string // 用户操作的动作名称,如:FavoriteNameActionLog 表示点赞相关操作
11 | SubName string // 用户操作动作的子名称,如:FavoriteUpActionLog 表示给视频增加赞操作
12 | ServiceName string // 服务来源,添加服务的名称,如 FavoriteService
13 | Attached string // 附带信息,当 Name - SubName 无法说明时,添加一个额外的信息
14 | ActorId uint32 // 操作者 Id
15 | VideoId uint32 // 附属的视频 Id,没有填写为0
16 | AffectUserId uint32 // 操作的用户 Id,如:被关注的用户 Id
17 | AffectAction uint // 操作的类型,如:1. 自增/自减某个数据,2. 直接修改某个数据
18 | AffectedData string // 操作的数值是什么,如果是自增,填 1,如果是修改为某个数据,那么填这个数据的值
19 | EventId string // 如果这个操作是一个大操作的子类型,那么需要具有相同的 UUID
20 | TraceId string // 这个操作的 TraceId
21 | SpanId string // 这个操作的 SpanId
22 | gorm.Model //数据库模型
23 | }
24 |
25 | func init() {
26 | if err := database.Client.AutoMigrate(&Action{}); err != nil {
27 | panic(err)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/models/comment.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type Comment struct {
9 | ID uint32 `gorm:"not null;primaryKey;autoIncrement"` // 评论 ID
10 | VideoId uint32 `json:"video_id" column:"video_id" gorm:"not null;index:comment_video"` // 视频 ID
11 | UserId uint32 `json:"user_id" column:"user_id" gorm:"not null"` // 用户 ID
12 | Content string `json:"content" column:"content"` // 评论内容
13 | Rate uint32 `gorm:"index:comment_video"`
14 | Reason string
15 | ModerationFlagged bool
16 | ModerationHate bool
17 | ModerationHateThreatening bool
18 | ModerationSelfHarm bool
19 | ModerationSexual bool
20 | ModerationSexualMinors bool
21 | ModerationViolence bool
22 | ModerationViolenceGraphic bool
23 | gorm.Model
24 | }
25 |
26 | func init() {
27 | if err := database.Client.AutoMigrate(&Comment{}); err != nil {
28 | panic(err)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/models/event.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type RecommendEvent struct {
4 | Type int // 1. 已读 2. 喜欢 3. 插入新数据
5 | Source string // 来源
6 | Slice string // 附带信息
7 | ActorId uint32
8 | VideoId []uint32 // 代表视频 Id,可以批量操作,但是仅对于某一个唯一的用户
9 | Tag []string // 插入时使用
10 | Category []string // 插入时使用
11 | Title string
12 | }
13 |
--------------------------------------------------------------------------------
/src/models/message.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "time"
6 |
7 | "gorm.io/gorm"
8 | )
9 |
10 | type Message struct {
11 | ID uint32 `gorm:"not null;primarykey;autoIncrement"`
12 | ToUserId uint32 `gorm:"not null"`
13 | FromUserId uint32 `gorm:"not null"`
14 | ConversationId string `gorm:"not null" index:"conversationid"`
15 | Content string `gorm:"not null"`
16 |
17 | // Create_time time.Time `gorm:"not null"`
18 | //Updatetime deleteTime
19 | gorm.Model
20 | }
21 |
22 | // es 使用
23 | type EsMessage struct {
24 | ToUserId uint32 `json:"toUserid"`
25 | FromUserId uint32 `json:"fromUserId"`
26 | ConversationId string `json:"conversationId"`
27 | Content string `json:"content"`
28 | CreateTime time.Time `json:"createTime"`
29 | }
30 |
31 | func init() {
32 | if err := database.Client.AutoMigrate(&Message{}); err != nil {
33 | panic(err)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/models/rawvideo.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type RawVideo struct {
9 | ActorId uint32
10 | VideoId uint32 `gorm:"not null;primaryKey"`
11 | Title string
12 | FileName string
13 | CoverName string
14 | gorm.Model
15 | }
16 |
17 | func init() {
18 | if err := database.Client.AutoMigrate(&RawVideo{}); err != nil {
19 | panic(err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/models/relation.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | )
7 |
8 | type Relation struct {
9 | ID uint32 `gorm:"not null;primarykey;autoIncrement"` // relation ID
10 | ActorId uint32 `json:"actor_id" column:"actor_id" gorm:"not null;index:actor_list"` // 粉丝 ID
11 | UserId uint32 `json:"user_id" column:"user_id" gorm:"not null;index:user_list"` // 被关注用户 ID
12 | gorm.Model
13 | }
14 |
15 | func init() {
16 | if err := database.Client.AutoMigrate(&Relation{}); err != nil {
17 | panic(err)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/models/user.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | "regexp"
7 | )
8 |
9 | type User struct {
10 | ID uint32 `gorm:"not null;primarykey;autoIncrement"` //用户 Id
11 | UserName string `gorm:"not null;unique;size: 32;index" redis:"UserName"` // 用户名
12 | Password string `gorm:"not null" redis:"Password"` // 密码
13 | Role int `gorm:"default:1" redis:"Role"` // 角色
14 | Avatar string `redis:"Avatar"` // 头像
15 | BackgroundImage string `redis:"BackGroundImage"` // 背景图片
16 | Signature string `redis:"Signature"` // 个人简介
17 | gorm.Model
18 | }
19 |
20 | // IsNameEmail 判断用户的名称是否为邮箱。
21 | func (u *User) IsNameEmail() bool {
22 | pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*`
23 | reg := regexp.MustCompile(pattern)
24 | return reg.MatchString(u.UserName)
25 | }
26 |
27 | func (u *User) IsDirty() bool {
28 | return u.UserName != ""
29 | }
30 |
31 | func (u *User) GetID() uint32 {
32 | return u.ID
33 | }
34 |
35 | func init() {
36 | if err := database.Client.AutoMigrate(&User{}); err != nil {
37 | panic(err)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/models/video.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/storage/database"
5 | "gorm.io/gorm"
6 | )
7 |
8 | // Video 视频表
9 | type Video struct {
10 | ID uint32 `gorm:"not null;primaryKey;"`
11 | UserId uint32 `json:"user_id" gorm:"not null;"`
12 | Title string `json:"title" gorm:"not null;"`
13 | FileName string `json:"play_name" gorm:"not null;"`
14 | CoverName string `json:"cover_name" gorm:"not null;"`
15 | AudioFileName string
16 | Transcript string
17 | Summary string
18 | Keywords string // e.g., "keywords1 | keywords2 | keywords3"
19 | gorm.Model
20 | }
21 |
22 | func init() {
23 | if err := database.Client.AutoMigrate(&Video{}); err != nil {
24 | panic(err)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/services/comment/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/comment"
8 | "GuGoTik/src/utils/consul"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/utils/prom"
11 | "context"
12 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
13 | "github.com/oklog/run"
14 | "github.com/prometheus/client_golang/prometheus/promhttp"
15 | "github.com/sirupsen/logrus"
16 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
17 | "google.golang.org/grpc"
18 | "google.golang.org/grpc/health"
19 | "google.golang.org/grpc/health/grpc_health_v1"
20 | "net"
21 | "net/http"
22 | "os"
23 | "syscall"
24 | )
25 |
26 | func main() {
27 | tp, err := tracing.SetTraceProvider(config.CommentRpcServerName)
28 |
29 | if err != nil {
30 | logging.Logger.WithFields(logrus.Fields{
31 | "err": err,
32 | }).Panicf("Error to set the trace")
33 | }
34 | defer func() {
35 | if err := tp.Shutdown(context.Background()); err != nil {
36 | logging.Logger.WithFields(logrus.Fields{
37 | "err": err,
38 | }).Errorf("Error to set the trace")
39 | }
40 | }()
41 |
42 | // Configure Pyroscope
43 | profiling.InitPyroscope("GuGoTik.CommentService")
44 |
45 | log := logging.LogService(config.CommentRpcServerName)
46 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.CommentRpcServerPort)
47 |
48 | if err != nil {
49 | log.Panicf("Rpc %s listen happens error: %v", config.CommentRpcServerName, err)
50 | }
51 |
52 | srvMetrics := grpcprom.NewServerMetrics(
53 | grpcprom.WithServerHandlingTimeHistogram(
54 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
55 | ),
56 | )
57 | reg := prom.Client
58 | reg.MustRegister(srvMetrics)
59 |
60 | s := grpc.NewServer(
61 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
62 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
63 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
64 | )
65 |
66 | if err := consul.RegisterConsul(config.CommentRpcServerName, config.CommentRpcServerPort); err != nil {
67 | log.Panicf("Rpc %s register consul happens error for: %v", config.CommentRpcServerName, err)
68 | }
69 | log.Infof("Rpc %s is running at %s now", config.CommentRpcServerName, config.CommentRpcServerPort)
70 |
71 | var srv CommentServiceImpl
72 | comment.RegisterCommentServiceServer(s, srv)
73 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
74 | defer CloseMQConn()
75 | if err := consul.RegisterConsul(config.CommentRpcServerName, config.CommentRpcServerPort); err != nil {
76 | log.Panicf("Rpc %s register consul happens error for: %v", config.CommentRpcServerName, err)
77 | }
78 | srv.New()
79 | srvMetrics.InitializeMetrics(s)
80 |
81 | g := &run.Group{}
82 | g.Add(func() error {
83 | return s.Serve(lis)
84 | }, func(err error) {
85 | s.GracefulStop()
86 | s.Stop()
87 | log.Errorf("Rpc %s listen happens error for: %v", config.CommentRpcServerName, err)
88 | })
89 |
90 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
91 | g.Add(func() error {
92 | m := http.NewServeMux()
93 | m.Handle("/metrics", promhttp.HandlerFor(
94 | reg,
95 | promhttp.HandlerOpts{
96 | EnableOpenMetrics: true,
97 | },
98 | ))
99 | httpSrv.Handler = m
100 | log.Infof("Promethus now running")
101 | return httpSrv.ListenAndServe()
102 | }, func(error) {
103 | if err := httpSrv.Close(); err != nil {
104 | log.Errorf("Prometheus %s listen happens error for: %v", config.CommentRpcServerName, err)
105 | }
106 | })
107 |
108 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
109 |
110 | if err := g.Run(); err != nil {
111 | log.WithFields(logrus.Fields{
112 | "err": err,
113 | }).Errorf("Error when runing http server")
114 | os.Exit(1)
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/services/comment/moderation.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "context"
7 | "errors"
8 | "github.com/sashabaranov/go-openai"
9 | "github.com/sirupsen/logrus"
10 | "go.opentelemetry.io/otel/trace"
11 | "net/http"
12 | url2 "net/url"
13 | "strconv"
14 | "strings"
15 | )
16 |
17 | var openaiClient *openai.Client
18 |
19 | func init() {
20 | cfg := openai.DefaultConfig(config.EnvCfg.ChatGPTAPIKEYS)
21 |
22 | url, err := url2.Parse(config.EnvCfg.ChatGptProxy)
23 | if err != nil {
24 | panic(err)
25 | }
26 | cfg.HTTPClient = &http.Client{
27 | Transport: &http.Transport{
28 | Proxy: http.ProxyURL(url),
29 | },
30 | }
31 |
32 | openaiClient = openai.NewClientWithConfig(cfg)
33 | }
34 |
35 | func RateCommentByGPT(commentContent string, logger *logrus.Entry, span trace.Span) (rate uint32, reason string, err error) {
36 | logger.WithFields(logrus.Fields{
37 | "comment_content": commentContent,
38 | }).Debugf("Start RateCommentByGPT")
39 |
40 | resp, err := openaiClient.CreateChatCompletion(
41 | context.Background(),
42 | openai.ChatCompletionRequest{
43 | Model: openai.GPT3Dot5Turbo,
44 | Messages: []openai.ChatCompletionMessage{
45 | {
46 | Role: openai.ChatMessageRoleSystem,
47 | Content: "According to the content of the user's reply or question and send back a number which is between 1 and 5. " +
48 | "The number is greater when the user's content involved the greater the degree of political leaning or unfriendly speech. " +
49 | "You should only reply such a number without any word else whatever user ask you. " +
50 | "Besides those, you should give the reason using Chinese why the message is unfriendly with details without revealing that you are divide the message into five number. " +
51 | "For example: user: 你是个大傻逼。 you: 4 | 用户尝试骂人,进行人格侮辱。user: 今天天气正好。 you: 1 | 用户正常聊天,无异常。",
52 | },
53 | {
54 | Role: openai.ChatMessageRoleUser,
55 | Content: commentContent,
56 | },
57 | },
58 | })
59 |
60 | if err != nil {
61 | logger.WithFields(logrus.Fields{
62 | "err": err,
63 | }).Errorf("ChatGPT request error")
64 | logging.SetSpanError(span, err)
65 |
66 | return
67 | }
68 |
69 | respContent := resp.Choices[0].Message.Content
70 |
71 | logger.WithFields(logrus.Fields{
72 | "resp": respContent,
73 | }).Debugf("Get ChatGPT response.")
74 |
75 | parts := strings.SplitN(respContent, " | ", 2)
76 |
77 | if len(parts) != 2 {
78 | logger.WithFields(logrus.Fields{
79 | "resp": respContent,
80 | }).Errorf("ChatGPT response does not match expected format")
81 | logging.SetSpanError(span, errors.New("ChatGPT response does not match expected format"))
82 |
83 | return
84 | }
85 |
86 | rateNum, err := strconv.ParseUint(parts[0], 10, 32)
87 | if err != nil {
88 | logger.WithFields(logrus.Fields{
89 | "resp": respContent,
90 | }).Errorf("ChatGPT response does not match expected format")
91 | logging.SetSpanError(span, errors.New("ChatGPT response does not match expected format"))
92 |
93 | return
94 | }
95 |
96 | rate = uint32(rateNum)
97 | reason = parts[1]
98 |
99 | return
100 | }
101 |
102 | func ModerationCommentByGPT(commentContent string, logger *logrus.Entry, span trace.Span) (moderationRes openai.Result) {
103 | logger.WithFields(logrus.Fields{
104 | "comment_content": commentContent,
105 | }).Debugf("Start ModerationCommentByGPT")
106 |
107 | resp, err := openaiClient.Moderations(
108 | context.Background(),
109 | openai.ModerationRequest{
110 | Model: openai.ModerationTextLatest,
111 | Input: commentContent,
112 | },
113 | )
114 |
115 | if err != nil {
116 | logger.WithFields(logrus.Fields{
117 | "err": err,
118 | }).Errorf("ChatGPT request error")
119 | logging.SetSpanError(span, err)
120 |
121 | return
122 | }
123 |
124 | moderationRes = resp.Results[0]
125 | return
126 | }
127 |
--------------------------------------------------------------------------------
/src/services/favorite/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/favorite"
8 | "GuGoTik/src/utils/audit"
9 | "GuGoTik/src/utils/consul"
10 | "GuGoTik/src/utils/logging"
11 | "GuGoTik/src/utils/prom"
12 | "context"
13 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
14 | "github.com/oklog/run"
15 | "github.com/prometheus/client_golang/prometheus/promhttp"
16 | "google.golang.org/grpc/health"
17 | "google.golang.org/grpc/health/grpc_health_v1"
18 | "net"
19 | "net/http"
20 | "os"
21 | "syscall"
22 |
23 | "github.com/sirupsen/logrus"
24 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
25 | "google.golang.org/grpc"
26 | )
27 |
28 | func main() {
29 | tp, err := tracing.SetTraceProvider(config.FavoriteRpcServerName)
30 |
31 | if err != nil {
32 | logging.Logger.WithFields(logrus.Fields{
33 | "err": err,
34 | }).Panicf("Error to set the trace")
35 | }
36 | defer func() {
37 | if err := tp.Shutdown(context.Background()); err != nil {
38 | logging.Logger.WithFields(logrus.Fields{
39 | "err": err,
40 | }).Errorf("Error to set the trace")
41 | }
42 | }()
43 |
44 | // Configure Pyroscope
45 | profiling.InitPyroscope("GuGoTik.FavoriteService")
46 |
47 | log := logging.LogService(config.FavoriteRpcServerName)
48 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.FavoriteRpcServerPort)
49 |
50 | if err != nil {
51 | log.Panicf("Rpc %s listen happens error: %v", config.FavoriteRpcServerName, err)
52 | }
53 |
54 | srvMetrics := grpcprom.NewServerMetrics(
55 | grpcprom.WithServerHandlingTimeHistogram(
56 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
57 | ),
58 | )
59 | reg := prom.Client
60 | reg.MustRegister(srvMetrics)
61 |
62 | s := grpc.NewServer(
63 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
64 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
65 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
66 | )
67 |
68 | if err := consul.RegisterConsul(config.FavoriteRpcServerName, config.FavoriteRpcServerPort); err != nil {
69 | log.Panicf("Rpc %s register consul happens error for: %v", config.FavoriteRpcServerName, err)
70 | }
71 | log.Infof("Rpc %s is running at %s now", config.FavoriteRpcServerName, config.FavoriteRpcServerPort)
72 |
73 | var srv FavoriteServiceServerImpl
74 | favorite.RegisterFavoriteServiceServer(s, srv)
75 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
76 | defer CloseMQConn()
77 | if err := consul.RegisterConsul(config.FavoriteRpcServerName, config.FavoriteRpcServerPort); err != nil {
78 | log.Panicf("Rpc %s register consul happens error for: %v", config.FavoriteRpcServerName, err)
79 | }
80 | srv.New()
81 |
82 | // Initialize the audit_exchange
83 | audit.DeclareAuditExchange(channel)
84 |
85 | srvMetrics.InitializeMetrics(s)
86 |
87 | g := &run.Group{}
88 | g.Add(func() error {
89 | return s.Serve(lis)
90 | }, func(err error) {
91 | s.GracefulStop()
92 | s.Stop()
93 | log.Errorf("Rpc %s listen happens error for: %v", config.FavoriteRpcServerName, err)
94 | })
95 |
96 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
97 | g.Add(func() error {
98 | m := http.NewServeMux()
99 | m.Handle("/metrics", promhttp.HandlerFor(
100 | reg,
101 | promhttp.HandlerOpts{
102 | EnableOpenMetrics: true,
103 | },
104 | ))
105 | httpSrv.Handler = m
106 | log.Infof("Promethus now running")
107 | return httpSrv.ListenAndServe()
108 | }, func(error) {
109 | if err := httpSrv.Close(); err != nil {
110 | log.Errorf("Prometheus %s listen happens error for: %v", config.FavoriteRpcServerName, err)
111 | }
112 | })
113 |
114 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
115 |
116 | if err := g.Run(); err != nil {
117 | log.WithFields(logrus.Fields{
118 | "err": err,
119 | }).Errorf("Error when runing http server")
120 | os.Exit(1)
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/src/services/feed/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/feed"
8 | "GuGoTik/src/utils/consul"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/utils/prom"
11 | "context"
12 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
13 | "github.com/oklog/run"
14 | "github.com/prometheus/client_golang/prometheus/promhttp"
15 | "github.com/sirupsen/logrus"
16 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
17 | "google.golang.org/grpc"
18 | "google.golang.org/grpc/health"
19 | "google.golang.org/grpc/health/grpc_health_v1"
20 | "net"
21 | "net/http"
22 | "os"
23 | "syscall"
24 | )
25 |
26 | func main() {
27 | tp, err := tracing.SetTraceProvider(config.FeedRpcServerName)
28 |
29 | if err != nil {
30 | logging.Logger.WithFields(logrus.Fields{
31 | "err": err,
32 | }).Panicf("Error to set the trace")
33 | }
34 | defer func() {
35 | if err := tp.Shutdown(context.Background()); err != nil {
36 | logging.Logger.WithFields(logrus.Fields{
37 | "err": err,
38 | }).Errorf("Error to set the trace")
39 | }
40 | }()
41 |
42 | // Configure Pyroscope
43 | profiling.InitPyroscope("GuGoTik.FeedService")
44 |
45 | log := logging.LogService(config.FeedRpcServerName)
46 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.FeedRpcServerPort)
47 |
48 | if err != nil {
49 | log.Panicf("Rpc %s listen happens error: %v", config.FeedRpcServerName, err)
50 | }
51 |
52 | srvMetrics := grpcprom.NewServerMetrics(
53 | grpcprom.WithServerHandlingTimeHistogram(
54 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
55 | ),
56 | )
57 | reg := prom.Client
58 | reg.MustRegister(srvMetrics)
59 |
60 | s := grpc.NewServer(
61 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
62 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
63 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
64 | )
65 |
66 | if err := consul.RegisterConsul(config.FeedRpcServerName, config.FeedRpcServerPort); err != nil {
67 | log.Panicf("Rpc %s register consul happens error for: %v", config.FeedRpcServerName, err)
68 | }
69 | log.Infof("Rpc %s is running at %s now", config.FeedRpcServerName, config.FeedRpcServerPort)
70 |
71 | var srv FeedServiceImpl
72 | feed.RegisterFeedServiceServer(s, srv)
73 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
74 | defer CloseMQConn()
75 | if err := consul.RegisterConsul(config.FeedRpcServerName, config.FeedRpcServerPort); err != nil {
76 | log.Panicf("Rpc %s register consul happens error for: %v", config.FeedRpcServerName, err)
77 | }
78 | srv.New()
79 | srvMetrics.InitializeMetrics(s)
80 |
81 | g := &run.Group{}
82 | g.Add(func() error {
83 | return s.Serve(lis)
84 | }, func(err error) {
85 | s.GracefulStop()
86 | s.Stop()
87 | log.Errorf("Rpc %s listen happens error for: %v", config.FeedRpcServerName, err)
88 | })
89 |
90 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
91 | g.Add(func() error {
92 | m := http.NewServeMux()
93 | m.Handle("/metrics", promhttp.HandlerFor(
94 | reg,
95 | promhttp.HandlerOpts{
96 | EnableOpenMetrics: true,
97 | },
98 | ))
99 | httpSrv.Handler = m
100 | log.Infof("Promethus now running")
101 | return httpSrv.ListenAndServe()
102 | }, func(error) {
103 | if err := httpSrv.Close(); err != nil {
104 | log.Errorf("Prometheus %s listen happens error for: %v", config.FeedRpcServerName, err)
105 | }
106 | })
107 |
108 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
109 |
110 | if err := g.Run(); err != nil {
111 | log.WithFields(logrus.Fields{
112 | "err": err,
113 | }).Errorf("Error when runing http server")
114 | os.Exit(1)
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/services/message/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/chat"
8 | "GuGoTik/src/utils/consul"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/utils/prom"
11 | "context"
12 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
13 | "github.com/oklog/run"
14 | "github.com/prometheus/client_golang/prometheus/promhttp"
15 | "google.golang.org/grpc/health"
16 | "google.golang.org/grpc/health/grpc_health_v1"
17 | "net"
18 | "net/http"
19 | "os"
20 | "syscall"
21 |
22 | "github.com/sirupsen/logrus"
23 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
24 | "google.golang.org/grpc"
25 | )
26 |
27 | func main() {
28 | tp, err := tracing.SetTraceProvider(config.MessageRpcServerName)
29 |
30 | if err != nil {
31 | logging.Logger.WithFields(logrus.Fields{
32 | "err": err,
33 | }).Panicf("Error to set the trace")
34 | }
35 | defer func() {
36 | if err := tp.Shutdown(context.Background()); err != nil {
37 | logging.Logger.WithFields(logrus.Fields{
38 | "err": err,
39 | }).Errorf("Error to set the trace")
40 | }
41 | }()
42 |
43 | // Configure Pyroscope
44 | profiling.InitPyroscope("GuGoTik.ChatService")
45 |
46 | log := logging.LogService(config.MessageRpcServerName)
47 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.MessageRpcServerPort)
48 |
49 | if err != nil {
50 | log.Panicf("Rpc %s listen happens error: %v", config.MessageRpcServerName, err)
51 | }
52 |
53 | srvMetrics := grpcprom.NewServerMetrics(
54 | grpcprom.WithServerHandlingTimeHistogram(
55 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
56 | ),
57 | )
58 |
59 | reg := prom.Client
60 | reg.MustRegister(srvMetrics)
61 | defer CloseMQConn()
62 | s := grpc.NewServer(
63 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
64 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
65 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
66 | )
67 |
68 | if err := consul.RegisterConsul(config.MessageRpcServerName, config.MessageRpcServerPort); err != nil {
69 | log.Panicf("Rpc %s register consul happens error for: %v", config.MessageRpcServerName, err)
70 | }
71 | log.Infof("Rpc %s is running at %s now", config.MessageRpcServerName, config.MessageRpcServerPort)
72 |
73 | var srv MessageServiceImpl
74 | chat.RegisterChatServiceServer(s, srv)
75 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
76 | defer CloseMQConn()
77 | srv.New()
78 | srvMetrics.InitializeMetrics(s)
79 |
80 | g := &run.Group{}
81 | g.Add(func() error {
82 | return s.Serve(lis)
83 | }, func(err error) {
84 | s.GracefulStop()
85 | s.Stop()
86 | log.Errorf("Rpc %s listen happens error for: %v", config.MessageRpcServerName, err)
87 | })
88 |
89 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
90 | g.Add(func() error {
91 | m := http.NewServeMux()
92 | m.Handle("/metrics", promhttp.HandlerFor(
93 | reg,
94 | promhttp.HandlerOpts{
95 | EnableOpenMetrics: true,
96 | },
97 | ))
98 | httpSrv.Handler = m
99 | log.Infof("Promethus now running")
100 | return httpSrv.ListenAndServe()
101 | }, func(error) {
102 | if err := httpSrv.Close(); err != nil {
103 | log.Errorf("Prometheus %s listen happens error for: %v", config.MessageRpcServerName, err)
104 | }
105 | })
106 |
107 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
108 |
109 | if err := g.Run(); err != nil {
110 | log.WithFields(logrus.Fields{
111 | "err": err,
112 | }).Errorf("Error when runing http server")
113 | os.Exit(1)
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/services/msgconsumer/esexchange.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/strings"
5 | "GuGoTik/src/extra/tracing"
6 | "GuGoTik/src/models"
7 | "GuGoTik/src/storage/es"
8 | "GuGoTik/src/utils/logging"
9 | "GuGoTik/src/utils/rabbitmq"
10 | "bytes"
11 | "context"
12 | "encoding/json"
13 |
14 | "github.com/elastic/go-elasticsearch/esapi"
15 | amqp "github.com/rabbitmq/amqp091-go"
16 | "github.com/sirupsen/logrus"
17 | )
18 |
19 | func esSaveMessage(channel *amqp.Channel) {
20 |
21 | msg, err := channel.Consume(strings.MessageES, "",
22 | false, false, false, false, nil,
23 | )
24 | failOnError(err, "Failed to Consume")
25 |
26 | var message models.Message
27 | for body := range msg {
28 | ctx := rabbitmq.ExtractAMQPHeaders(context.Background(), body.Headers)
29 | ctx, span := tracing.Tracer.Start(ctx, "MessageSendService")
30 | logger := logging.LogService("MessageSend").WithContext(ctx)
31 |
32 | if err := json.Unmarshal(body.Body, &message); err != nil {
33 | logger.WithFields(logrus.Fields{
34 | "from_id": message.FromUserId,
35 | "to_id": message.ToUserId,
36 | "content": message.Content,
37 | "err": err,
38 | }).Errorf("Error when unmarshaling the prepare json body.")
39 | logging.SetSpanError(span, err)
40 | err = body.Nack(false, true)
41 | if err != nil {
42 | logger.WithFields(
43 | logrus.Fields{
44 | "from_id": message.FromUserId,
45 | "to_id": message.ToUserId,
46 | "content": message.Content,
47 | "err": err,
48 | },
49 | ).Errorf("Error when nack the message")
50 | logging.SetSpanError(span, err)
51 | }
52 | span.End()
53 | continue
54 | }
55 |
56 | EsMessage := models.EsMessage{
57 | ToUserId: message.ToUserId,
58 | FromUserId: message.FromUserId,
59 | ConversationId: message.ConversationId,
60 | Content: message.Content,
61 | CreateTime: message.CreatedAt,
62 | }
63 | data, _ := json.Marshal(EsMessage)
64 |
65 | req := esapi.IndexRequest{
66 | Index: "message",
67 | Refresh: "true",
68 | Body: bytes.NewReader(data),
69 | }
70 | //返回值close
71 | res, err := req.Do(ctx, es.EsClient)
72 |
73 | if err != nil {
74 | logger.WithFields(logrus.Fields{
75 | "from_id": message.FromUserId,
76 | "to_id": message.ToUserId,
77 | "content": message.Content,
78 | "err": err,
79 | }).Errorf("Error when insert message to database.")
80 | logging.SetSpanError(span, err)
81 |
82 | span.End()
83 | continue
84 | }
85 | res.Body.Close()
86 |
87 | err = body.Ack(false)
88 |
89 | if err != nil {
90 | logger.WithFields(logrus.Fields{
91 | "err": err,
92 | }).Errorf("Error when dealing with the message...3")
93 | logging.SetSpanError(span, err)
94 | }
95 |
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/services/publish/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/publish"
8 | "GuGoTik/src/utils/consul"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/utils/prom"
11 | "context"
12 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
13 | "github.com/oklog/run"
14 | "github.com/prometheus/client_golang/prometheus/promhttp"
15 | "github.com/sirupsen/logrus"
16 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
17 | "google.golang.org/grpc"
18 | "google.golang.org/grpc/health"
19 | "google.golang.org/grpc/health/grpc_health_v1"
20 | "net"
21 | "net/http"
22 | "os"
23 | "syscall"
24 | )
25 |
26 | func main() {
27 | tp, err := tracing.SetTraceProvider(config.PublishRpcServerName)
28 |
29 | if err != nil {
30 | logging.Logger.WithFields(logrus.Fields{
31 | "err": err,
32 | }).Panicf("Error to set the trace")
33 | }
34 | defer func() {
35 | if err := tp.Shutdown(context.Background()); err != nil {
36 | logging.Logger.WithFields(logrus.Fields{
37 | "err": err,
38 | }).Errorf("Error to set the trace")
39 | }
40 | }()
41 |
42 | // Configure Pyroscope
43 | profiling.InitPyroscope("GuGoTik.PublishService")
44 |
45 | log := logging.LogService(config.PublishRpcServerName)
46 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.PublishRpcServerPort)
47 |
48 | if err != nil {
49 | log.Panicf("Rpc %s listen happens error: %v", config.PublishRpcServerName, err)
50 | }
51 |
52 | srvMetrics := grpcprom.NewServerMetrics(
53 | grpcprom.WithServerHandlingTimeHistogram(
54 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
55 | ),
56 | )
57 | reg := prom.Client
58 | reg.MustRegister(srvMetrics)
59 | maxSize := config.MaxVideoSize
60 |
61 | s := grpc.NewServer(
62 | grpc.MaxRecvMsgSize(maxSize),
63 | grpc.MaxSendMsgSize(maxSize),
64 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
65 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
66 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
67 | )
68 |
69 | if err := consul.RegisterConsul(config.PublishRpcServerName, config.PublishRpcServerPort); err != nil {
70 | log.Panicf("Rpc %s register consul happens error for: %v", config.PublishRpcServerName, err)
71 | }
72 | log.Infof("Rpc %s is running at %s now", config.PublishRpcServerName, config.PublishRpcServerPort)
73 |
74 | var srv PublishServiceImpl
75 | publish.RegisterPublishServiceServer(s, srv)
76 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
77 | defer CloseMQConn()
78 | if err := consul.RegisterConsul(config.PublishRpcServerName, config.PublishRpcServerPort); err != nil {
79 | log.Panicf("Rpc %s register consul happens error for: %v", config.PublishRpcServerName, err)
80 | }
81 | srv.New()
82 | srvMetrics.InitializeMetrics(s)
83 |
84 | g := &run.Group{}
85 | g.Add(func() error {
86 | return s.Serve(lis)
87 | }, func(err error) {
88 | s.GracefulStop()
89 | s.Stop()
90 | log.Errorf("Rpc %s listen happens error for: %v", config.PublishRpcServerName, err)
91 | })
92 |
93 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
94 | g.Add(func() error {
95 | m := http.NewServeMux()
96 | m.Handle("/metrics", promhttp.HandlerFor(
97 | reg,
98 | promhttp.HandlerOpts{
99 | EnableOpenMetrics: true,
100 | },
101 | ))
102 | httpSrv.Handler = m
103 | log.Infof("Promethus now running")
104 | return httpSrv.ListenAndServe()
105 | }, func(error) {
106 | if err := httpSrv.Close(); err != nil {
107 | log.Errorf("Prometheus %s listen happens error for: %v", config.PublishRpcServerName, err)
108 | }
109 | })
110 |
111 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
112 |
113 | if err := g.Run(); err != nil {
114 | log.WithFields(logrus.Fields{
115 | "err": err,
116 | }).Errorf("Error when runing http server")
117 | os.Exit(1)
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/services/recommend/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/recommend"
8 | "GuGoTik/src/utils/consul"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/utils/prom"
11 | "context"
12 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
13 | "github.com/oklog/run"
14 | "github.com/prometheus/client_golang/prometheus/promhttp"
15 | "github.com/sirupsen/logrus"
16 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
17 | "google.golang.org/grpc"
18 | "google.golang.org/grpc/health"
19 | "google.golang.org/grpc/health/grpc_health_v1"
20 | "net"
21 | "net/http"
22 | "os"
23 | "syscall"
24 | )
25 |
26 | func main() {
27 | tp, err := tracing.SetTraceProvider(config.RecommendRpcServiceName)
28 |
29 | if err != nil {
30 | logging.Logger.WithFields(logrus.Fields{
31 | "err": err,
32 | }).Panicf("Error to set the trace")
33 | }
34 | defer func() {
35 | if err := tp.Shutdown(context.Background()); err != nil {
36 | logging.Logger.WithFields(logrus.Fields{
37 | "err": err,
38 | }).Errorf("Error to set the trace")
39 | }
40 | }()
41 |
42 | // Configure Pyroscope
43 | profiling.InitPyroscope("GuGoTik.RecommendService")
44 |
45 | log := logging.LogService(config.RecommendRpcServiceName)
46 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.RecommendRpcServicePort)
47 |
48 | if err != nil {
49 | log.Panicf("Rpc %s listen happens error: %v", config.RecommendRpcServiceName, err)
50 | }
51 |
52 | srvMetrics := grpcprom.NewServerMetrics(
53 | grpcprom.WithServerHandlingTimeHistogram(
54 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
55 | ),
56 | )
57 |
58 | reg := prom.Client
59 | reg.MustRegister(srvMetrics)
60 |
61 | s := grpc.NewServer(
62 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
63 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
64 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
65 | )
66 |
67 | if err := consul.RegisterConsul(config.RecommendRpcServiceName, config.RecommendRpcServicePort); err != nil {
68 | log.Panicf("Rpc %s register consul happens error for: %v", config.RecommendRpcServiceName, err)
69 | }
70 | log.Infof("Rpc %s is running at %s now", config.RecommendRpcServiceName, config.RecommendRpcServicePort)
71 |
72 | var srv RecommendServiceImpl
73 | recommend.RegisterRecommendServiceServer(s, srv)
74 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
75 |
76 | srv.New()
77 | srvMetrics.InitializeMetrics(s)
78 |
79 | g := &run.Group{}
80 | g.Add(func() error {
81 | return s.Serve(lis)
82 | }, func(err error) {
83 | s.GracefulStop()
84 | s.Stop()
85 | log.Errorf("Rpc %s listen happens error for: %v", config.RecommendRpcServiceName, err)
86 | })
87 |
88 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
89 | g.Add(func() error {
90 | m := http.NewServeMux()
91 | m.Handle("/metrics", promhttp.HandlerFor(
92 | reg,
93 | promhttp.HandlerOpts{
94 | EnableOpenMetrics: true,
95 | },
96 | ))
97 | httpSrv.Handler = m
98 | log.Infof("Promethus now running")
99 | return httpSrv.ListenAndServe()
100 | }, func(error) {
101 | if err := httpSrv.Close(); err != nil {
102 | log.Errorf("Prometheus %s listen happens error for: %v", config.RecommendRpcServiceName, err)
103 | }
104 | })
105 |
106 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
107 |
108 | if err := g.Run(); err != nil {
109 | log.WithFields(logrus.Fields{
110 | "err": err,
111 | }).Errorf("Error when runing http server")
112 | os.Exit(1)
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/services/relation/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/relation"
8 | "GuGoTik/src/utils/audit"
9 | "GuGoTik/src/utils/consul"
10 | "GuGoTik/src/utils/logging"
11 | "GuGoTik/src/utils/prom"
12 | "context"
13 | grpcprom "github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus"
14 | "github.com/oklog/run"
15 | "github.com/prometheus/client_golang/prometheus/promhttp"
16 | amqp "github.com/rabbitmq/amqp091-go"
17 | "github.com/sirupsen/logrus"
18 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
19 | "google.golang.org/grpc"
20 | "google.golang.org/grpc/health"
21 | "google.golang.org/grpc/health/grpc_health_v1"
22 | "net"
23 | "net/http"
24 | "os"
25 | "syscall"
26 | )
27 |
28 | var conn = &amqp.Connection{}
29 | var channel = &amqp.Channel{}
30 |
31 | func main() {
32 | tp, err := tracing.SetTraceProvider(config.RelationRpcServerName)
33 |
34 | if err != nil {
35 | logging.Logger.WithFields(logrus.Fields{
36 | "err": err,
37 | }).Panicf("Error to set the trace")
38 | }
39 | defer func() {
40 | if err := tp.Shutdown(context.Background()); err != nil {
41 | logging.Logger.WithFields(logrus.Fields{
42 | "err": err,
43 | }).Errorf("Error to set the trace")
44 | }
45 | }()
46 |
47 | // Configure Pyroscope
48 | profiling.InitPyroscope("GuGoTik.RelationService")
49 |
50 | log := logging.LogService(config.RelationRpcServerName)
51 | lis, err := net.Listen("tcp", config.EnvCfg.PodIpAddr+config.RelationRpcServerPort)
52 |
53 | if err != nil {
54 | log.Panicf("Rpc %s listen happens error: %v", config.RelationRpcServerName, err)
55 | }
56 |
57 | srvMetrics := grpcprom.NewServerMetrics(
58 | grpcprom.WithServerHandlingTimeHistogram(
59 | grpcprom.WithHistogramBuckets([]float64{0.001, 0.01, 0.1, 0.3, 0.6, 1, 3, 6, 9, 20, 30, 60, 90, 120}),
60 | ),
61 | )
62 |
63 | reg := prom.Client
64 | reg.MustRegister(srvMetrics)
65 |
66 | s := grpc.NewServer(
67 | grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor()),
68 | grpc.ChainUnaryInterceptor(srvMetrics.UnaryServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
69 | grpc.ChainStreamInterceptor(srvMetrics.StreamServerInterceptor(grpcprom.WithExemplarFromContext(prom.ExtractContext))),
70 | )
71 |
72 | if err := consul.RegisterConsul(config.RelationRpcServerName, config.RelationRpcServerPort); err != nil {
73 | log.Panicf("Rpc %s register consul happens error for: %v", config.RelationRpcServerName, err)
74 | }
75 | log.Infof("Rpc %s is running at %s now", config.RelationRpcServerName, config.RelationRpcServerPort)
76 |
77 | var srv RelationServiceImpl
78 | relation.RegisterRelationServiceServer(s, srv)
79 | grpc_health_v1.RegisterHealthServer(s, health.NewServer())
80 |
81 | srv.New()
82 |
83 | // Initialize the audit_exchange
84 | audit.DeclareAuditExchange(channel)
85 | defer CloseMQConn()
86 |
87 | srvMetrics.InitializeMetrics(s)
88 |
89 | g := &run.Group{}
90 | g.Add(func() error {
91 | return s.Serve(lis)
92 | }, func(err error) {
93 | s.GracefulStop()
94 | s.Stop()
95 | log.Errorf("Rpc %s listen happens error for: %v", config.RelationRpcServerName, err)
96 | })
97 |
98 | httpSrv := &http.Server{Addr: config.EnvCfg.PodIpAddr + config.Metrics}
99 | g.Add(func() error {
100 | m := http.NewServeMux()
101 | m.Handle("/metrics", promhttp.HandlerFor(
102 | reg,
103 | promhttp.HandlerOpts{
104 | EnableOpenMetrics: true,
105 | },
106 | ))
107 | httpSrv.Handler = m
108 | log.Infof("Promethus now running")
109 | return httpSrv.ListenAndServe()
110 | }, func(error) {
111 | if err := httpSrv.Close(); err != nil {
112 | log.Errorf("Prometheus %s listen happens error for: %v", config.RelationRpcServerName, err)
113 | }
114 | })
115 |
116 | g.Add(run.SignalHandler(context.Background(), syscall.SIGINT, syscall.SIGTERM))
117 |
118 | if err := g.Run(); err != nil {
119 | log.WithFields(logrus.Fields{
120 | "err": err,
121 | }).Errorf("Error when runing http server")
122 | os.Exit(1)
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/storage/cached/ticker.go:
--------------------------------------------------------------------------------
1 | package cached
2 |
3 | import (
4 | "GuGoTik/src/storage/redis"
5 | "GuGoTik/src/utils/logging"
6 | redis2 "github.com/redis/go-redis/v9"
7 | "github.com/sirupsen/logrus"
8 | "time"
9 | )
10 |
11 | type TimeTicker struct {
12 | Tick *time.Ticker
13 | Work func(client redis2.UniversalClient) error
14 | }
15 |
16 | func (t *TimeTicker) Start() {
17 | for range t.Tick.C {
18 | err := t.Work(redis.Client)
19 | if err != nil {
20 | logging.Logger.WithFields(logrus.Fields{
21 | "err": err,
22 | }).Errorf("Error happens when dealing with cron job")
23 | continue
24 | }
25 | }
26 | }
27 |
28 | func NewTick(interval time.Duration, f func(client redis2.UniversalClient) error) *TimeTicker {
29 | return &TimeTicker{
30 | Tick: time.NewTicker(interval),
31 | Work: f,
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/storage/database/gorm.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "fmt"
7 | "gorm.io/driver/postgres"
8 | "gorm.io/gorm"
9 | "gorm.io/gorm/schema"
10 | "gorm.io/plugin/dbresolver"
11 | "gorm.io/plugin/opentelemetry/tracing"
12 | "strings"
13 | "time"
14 | )
15 |
16 | var Client *gorm.DB
17 |
18 | func init() {
19 | var err error
20 |
21 | gormLogrus := logging.GetGormLogger()
22 |
23 | var cfg gorm.Config
24 | if config.EnvCfg.PostgreSQLSchema == "" {
25 | cfg = gorm.Config{
26 | PrepareStmt: true,
27 | Logger: gormLogrus,
28 | NamingStrategy: schema.NamingStrategy{
29 | TablePrefix: config.EnvCfg.PostgreSQLSchema + "." + config.EnvCfg.PostgreSQLPrefix,
30 | },
31 | }
32 | } else {
33 | cfg = gorm.Config{
34 | PrepareStmt: true,
35 | Logger: gormLogrus,
36 | NamingStrategy: schema.NamingStrategy{
37 | TablePrefix: config.EnvCfg.PostgreSQLSchema + "." + config.EnvCfg.PostgreSQLPrefix,
38 | },
39 | }
40 | }
41 |
42 | if Client, err = gorm.Open(
43 | postgres.Open(
44 | fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s",
45 | config.EnvCfg.PostgreSQLHost,
46 | config.EnvCfg.PostgreSQLUser,
47 | config.EnvCfg.PostgreSQLPassword,
48 | config.EnvCfg.PostgreSQLDataBase,
49 | config.EnvCfg.PostgreSQLPort)),
50 | &cfg,
51 | ); err != nil {
52 | panic(err)
53 | }
54 |
55 | if config.EnvCfg.PostgreSQLReplicaState == "enable" {
56 | var replicas []gorm.Dialector
57 | for _, addr := range strings.Split(config.EnvCfg.PostgreSQLReplicaAddress, ",") {
58 | pair := strings.Split(addr, ":")
59 | if len(pair) != 2 {
60 | continue
61 | }
62 |
63 | replicas = append(replicas, postgres.Open(
64 | fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s",
65 | pair[0],
66 | config.EnvCfg.PostgreSQLReplicaUsername,
67 | config.EnvCfg.PostgreSQLReplicaPassword,
68 | config.EnvCfg.PostgreSQLDataBase,
69 | pair[1])))
70 | }
71 |
72 | err := Client.Use(dbresolver.Register(dbresolver.Config{
73 | Replicas: replicas,
74 | Policy: dbresolver.RandomPolicy{},
75 | }))
76 | if err != nil {
77 | panic(err)
78 | }
79 | }
80 |
81 | sqlDB, err := Client.DB()
82 | if err != nil {
83 | panic(err)
84 | }
85 |
86 | sqlDB.SetMaxIdleConns(100)
87 | sqlDB.SetMaxOpenConns(200)
88 | sqlDB.SetConnMaxLifetime(24 * time.Hour)
89 | sqlDB.SetConnMaxIdleTime(time.Hour)
90 |
91 | if err := Client.Use(tracing.NewPlugin()); err != nil {
92 | panic(err)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/storage/es/Elasticsearch.go:
--------------------------------------------------------------------------------
1 | package es
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "log"
6 |
7 | es "github.com/elastic/go-elasticsearch/v7"
8 | )
9 |
10 | var EsClient *es.Client
11 |
12 | func init() {
13 | cfg := es.Config{
14 | Addresses: []string{
15 | config.EnvCfg.ElasticsearchUrl,
16 | },
17 | }
18 | var err error
19 | EsClient, err = es.NewClient(cfg)
20 | if err != nil {
21 | log.Fatalf("elasticsearch.NewClient: %v", err)
22 | }
23 |
24 | _, err = EsClient.Info()
25 | if err != nil {
26 | log.Fatalf("Error getting response: %s", err)
27 | }
28 |
29 | _, err = EsClient.API.Indices.Create("Message")
30 |
31 | if err != nil {
32 | log.Fatalf("create index error: %s", err)
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/storage/file/fs.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/tracing"
6 | "GuGoTik/src/utils/logging"
7 | "context"
8 | "github.com/sirupsen/logrus"
9 | "io"
10 | "net/url"
11 | "os"
12 | "path"
13 | )
14 |
15 | type FSStorage struct {
16 | }
17 |
18 | func (f FSStorage) GetLocalPath(ctx context.Context, fileName string) string {
19 | _, span := tracing.Tracer.Start(ctx, "FSStorage-GetLocalPath")
20 | defer span.End()
21 | logging.SetSpanWithHostname(span)
22 | return path.Join(config.EnvCfg.FileSystemStartPath, fileName)
23 | }
24 |
25 | func (f FSStorage) Upload(ctx context.Context, fileName string, content io.Reader) (output *PutObjectOutput, err error) {
26 | ctx, span := tracing.Tracer.Start(ctx, "FSStorage-Upload")
27 | defer span.End()
28 | logging.SetSpanWithHostname(span)
29 | logger := logging.LogService("FSStorage.Upload").WithContext(ctx)
30 |
31 | logger = logger.WithFields(logrus.Fields{
32 | "file_name": fileName,
33 | })
34 | logger.Debugf("Process start")
35 |
36 | all, err := io.ReadAll(content)
37 | if err != nil {
38 | logger.WithFields(logrus.Fields{
39 | "err": err,
40 | }).Debug("Failed reading content")
41 | return nil, err
42 | }
43 |
44 | filePath := path.Join(config.EnvCfg.FileSystemStartPath, fileName)
45 |
46 | dir := path.Dir(filePath)
47 | err = os.MkdirAll(dir, os.FileMode(0755))
48 |
49 | if err != nil {
50 | logger.WithFields(logrus.Fields{
51 | "err": err,
52 | }).Debug("Failed creating directory before writing file")
53 | return nil, err
54 | }
55 |
56 | err = os.WriteFile(filePath, all, os.FileMode(0755))
57 |
58 | if err != nil {
59 | logger.WithFields(logrus.Fields{
60 | "err": err,
61 | }).Debug("Failed writing content to file")
62 | return nil, err
63 | }
64 |
65 | return &PutObjectOutput{}, nil
66 | }
67 |
68 | func (f FSStorage) GetLink(ctx context.Context, fileName string) (string, error) {
69 | _, span := tracing.Tracer.Start(ctx, "FSStorage-GetLink")
70 | defer span.End()
71 | logging.SetSpanWithHostname(span)
72 | return url.JoinPath(config.EnvCfg.FileSystemBaseUrl, fileName)
73 | }
74 |
75 | func (f FSStorage) IsFileExist(ctx context.Context, fileName string) (bool, error) {
76 | _, span := tracing.Tracer.Start(ctx, "FSStorage-IsFileExist")
77 | defer span.End()
78 | logging.SetSpanWithHostname(span)
79 | filePath := path.Join(config.EnvCfg.FileSystemStartPath, fileName)
80 | _, err := os.Stat(filePath)
81 | if err == nil {
82 | return true, nil
83 | }
84 | if os.IsNotExist(err) {
85 | return false, nil
86 | }
87 |
88 | return false, err
89 | }
90 |
--------------------------------------------------------------------------------
/src/storage/file/provider.go:
--------------------------------------------------------------------------------
1 | package file
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "context"
6 | "fmt"
7 | "io"
8 | )
9 |
10 | var client storageProvider
11 |
12 | type storageProvider interface {
13 | Upload(ctx context.Context, fileName string, content io.Reader) (*PutObjectOutput, error)
14 | GetLink(ctx context.Context, fileName string) (string, error)
15 | GetLocalPath(ctx context.Context, fileName string) string
16 | IsFileExist(ctx context.Context, fileName string) (bool, error)
17 | }
18 |
19 | type PutObjectOutput struct{}
20 |
21 | func init() {
22 | switch config.EnvCfg.StorageType { // Append more type here to provide more file action ability
23 | case "fs":
24 | client = FSStorage{}
25 | }
26 | }
27 |
28 | func Upload(ctx context.Context, fileName string, content io.Reader) (*PutObjectOutput, error) {
29 | return client.Upload(ctx, fileName, content)
30 | }
31 |
32 | func GetLocalPath(ctx context.Context, fileName string) string {
33 | return client.GetLocalPath(ctx, fileName)
34 | }
35 |
36 | func GetLink(ctx context.Context, fileName string, userId uint32) (link string, err error) {
37 | originLink, err := client.GetLink(ctx, fileName)
38 | link = fmt.Sprintf("%s?user_id=%d", originLink, userId)
39 | return
40 | }
41 |
42 | func IsFileExist(ctx context.Context, fileName string) (bool, error) {
43 | return client.IsFileExist(ctx, fileName)
44 | }
45 |
--------------------------------------------------------------------------------
/src/storage/mq/rabbitmq.go:
--------------------------------------------------------------------------------
1 | package mq
2 |
--------------------------------------------------------------------------------
/src/storage/redis/redis.go:
--------------------------------------------------------------------------------
1 | package redis
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "github.com/redis/go-redis/extra/redisotel/v9"
6 | "github.com/redis/go-redis/v9"
7 | "strings"
8 | )
9 |
10 | var Client redis.UniversalClient
11 |
12 | func init() {
13 | addrs := strings.Split(config.EnvCfg.RedisAddr, ";")
14 | Client = redis.NewUniversalClient(&redis.UniversalOptions{
15 | Addrs: addrs,
16 | Password: config.EnvCfg.RedisPassword,
17 | DB: config.EnvCfg.RedisDB,
18 | MasterName: config.EnvCfg.RedisMaster,
19 | })
20 |
21 | if err := redisotel.InstrumentTracing(Client); err != nil {
22 | panic(err)
23 | }
24 |
25 | if err := redisotel.InstrumentMetrics(Client); err != nil {
26 | panic(err)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/utils/audit/publish.go:
--------------------------------------------------------------------------------
1 | package audit
2 |
3 | import (
4 | "GuGoTik/src/constant/strings"
5 | "GuGoTik/src/extra/tracing"
6 | models2 "GuGoTik/src/models"
7 | "GuGoTik/src/utils/logging"
8 | "GuGoTik/src/utils/rabbitmq"
9 | "context"
10 | "encoding/json"
11 | amqp "github.com/rabbitmq/amqp091-go"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func exitOnError(err error) {
16 | if err != nil {
17 | panic(err)
18 | }
19 | }
20 |
21 | func DeclareAuditExchange(channel *amqp.Channel) {
22 | err := channel.ExchangeDeclare(
23 | strings.AuditExchange,
24 | "direct",
25 | true,
26 | false,
27 | false,
28 | false,
29 | nil,
30 | )
31 | exitOnError(err)
32 | }
33 |
34 | func PublishAuditEvent(ctx context.Context, action *models2.Action, channel *amqp.Channel) {
35 | ctx, span := tracing.Tracer.Start(ctx, "AuditEventPublisher")
36 | defer span.End()
37 | logging.SetSpanWithHostname(span)
38 | logger := logging.LogService("AuditEventPublisher").WithContext(ctx)
39 |
40 | data, err := json.Marshal(action)
41 | if err != nil {
42 | logger.WithFields(logrus.Fields{
43 | "err": err,
44 | }).Errorf("Error when marshal the action model")
45 | logging.SetSpanError(span, err)
46 | return
47 | }
48 |
49 | headers := rabbitmq.InjectAMQPHeaders(ctx)
50 |
51 | err = channel.PublishWithContext(ctx,
52 | strings.AuditExchange,
53 | strings.AuditPublishEvent,
54 | false,
55 | false,
56 | amqp.Publishing{
57 | ContentType: "text/plain",
58 | Body: data,
59 | Headers: headers,
60 | },
61 | )
62 |
63 | if err != nil {
64 | logger.WithFields(logrus.Fields{
65 | "err": err,
66 | }).Errorf("Error when publishing the action model")
67 | logging.SetSpanError(span, err)
68 | return
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/src/utils/consul/register.go:
--------------------------------------------------------------------------------
1 | package consul
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "fmt"
7 | "github.com/google/uuid"
8 | capi "github.com/hashicorp/consul/api"
9 | log "github.com/sirupsen/logrus"
10 | "strconv"
11 | )
12 |
13 | var consulClient *capi.Client
14 |
15 | func init() {
16 | cfg := capi.DefaultConfig()
17 | cfg.Address = config.EnvCfg.ConsulAddr
18 | if c, err := capi.NewClient(cfg); err == nil {
19 | consulClient = c
20 | return
21 | } else {
22 | logging.Logger.Panicf("Connect Consul happens error: %v", err)
23 | }
24 | }
25 |
26 | func RegisterConsul(name string, port string) error {
27 | parsedPort, err := strconv.Atoi(port[1:]) // port start with ':' which like ':37001'
28 | logging.Logger.WithFields(log.Fields{
29 | "name": name,
30 | "port": parsedPort,
31 | }).Infof("Services Register Consul")
32 | name = config.EnvCfg.ConsulAnonymityPrefix + name
33 |
34 | if err != nil {
35 | return err
36 | }
37 | reg := &capi.AgentServiceRegistration{
38 | ID: fmt.Sprintf("%s-%s", name, uuid.New().String()[:5]),
39 | Name: name,
40 | Address: config.EnvCfg.PodIpAddr,
41 | Port: parsedPort,
42 | Check: &capi.AgentServiceCheck{
43 | Interval: "5s",
44 | Timeout: "5s",
45 | GRPC: fmt.Sprintf("%s:%d", config.EnvCfg.PodIpAddr, parsedPort),
46 | GRPCUseTLS: false,
47 | DeregisterCriticalServiceAfter: "30s",
48 | },
49 | }
50 | if err := consulClient.Agent().ServiceRegister(reg); err != nil {
51 | return err
52 | }
53 | return nil
54 | }
55 |
--------------------------------------------------------------------------------
/src/utils/grpc/connection.go:
--------------------------------------------------------------------------------
1 | package grpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/utils/logging"
6 | "fmt"
7 | _ "github.com/mbobakov/grpc-consul-resolver"
8 | "github.com/sirupsen/logrus"
9 | "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/credentials/insecure"
12 | "google.golang.org/grpc/keepalive"
13 | "time"
14 | )
15 |
16 | func Connect(serviceName string) (conn *grpc.ClientConn) {
17 | kacp := keepalive.ClientParameters{
18 | Time: 10 * time.Second, // send pings every 10 seconds if there is no activity
19 | Timeout: time.Second, // wait 1 second for ping ack before considering the connection dead
20 | PermitWithoutStream: false, // send pings even without active streams
21 | }
22 |
23 | conn, err := grpc.Dial(
24 | fmt.Sprintf("consul://%s/%s?wait=15s", config.EnvCfg.ConsulAddr, config.EnvCfg.ConsulAnonymityPrefix+serviceName),
25 | grpc.WithTransportCredentials(insecure.NewCredentials()),
26 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`),
27 | grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor()),
28 | grpc.WithKeepaliveParams(kacp),
29 | )
30 |
31 | logging.Logger.Debugf("connect")
32 |
33 | if err != nil {
34 | logging.Logger.WithFields(logrus.Fields{
35 | "service": config.EnvCfg.ConsulAnonymityPrefix + serviceName,
36 | "err": err,
37 | }).Errorf("Cannot connect to %v service", config.EnvCfg.ConsulAnonymityPrefix+serviceName)
38 | }
39 | return
40 | }
41 |
--------------------------------------------------------------------------------
/src/utils/logging/gorm.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "github.com/sirupsen/logrus"
7 | "gorm.io/gorm/logger"
8 | "gorm.io/gorm/utils"
9 | "time"
10 | )
11 |
12 | var errRecordNotFound = errors.New("record not found")
13 |
14 | type GormLogger struct{}
15 |
16 | func (g GormLogger) LogMode(_ logger.LogLevel) logger.Interface {
17 | // We do not use this because Gorm will print different log according to log set.
18 | // However, we just print to TRACE.
19 | return g
20 | }
21 |
22 | func (g GormLogger) Info(ctx context.Context, s string, i ...interface{}) {
23 | Logger.WithContext(ctx).WithFields(logrus.Fields{
24 | "component": "gorm",
25 | }).Infof(s, i...)
26 | }
27 |
28 | func (g GormLogger) Warn(ctx context.Context, s string, i ...interface{}) {
29 | Logger.WithContext(ctx).WithFields(logrus.Fields{
30 | "component": "gorm",
31 | }).Warnf(s, i...)
32 | }
33 |
34 | func (g GormLogger) Error(ctx context.Context, s string, i ...interface{}) {
35 | Logger.WithContext(ctx).WithFields(logrus.Fields{
36 | "component": "gorm",
37 | }).Errorf(s, i...)
38 | }
39 |
40 | func (g GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {
41 | const traceStr = "File: %s, Cost: %v, Rows: %v, SQL: %s"
42 | elapsed := time.Since(begin)
43 | sql, rows := fc()
44 | fields := logrus.Fields{
45 | "component": "gorm",
46 | }
47 | if err != nil && !errors.Is(err, errRecordNotFound) {
48 | fields = logrus.Fields{
49 | "err": err,
50 | }
51 | }
52 |
53 | if rows == -1 {
54 | Logger.WithContext(ctx).WithFields(fields).Tracef(traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql)
55 | } else {
56 | Logger.WithContext(ctx).WithFields(fields).Tracef(traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
57 | }
58 | }
59 |
60 | func GetGormLogger() *GormLogger {
61 | return &GormLogger{}
62 | }
63 |
--------------------------------------------------------------------------------
/src/utils/logging/logging.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "fmt"
6 | log "github.com/sirupsen/logrus"
7 | "go.opentelemetry.io/otel/attribute"
8 | "go.opentelemetry.io/otel/codes"
9 | "go.opentelemetry.io/otel/trace"
10 | "io"
11 | "os"
12 | "path"
13 | )
14 |
15 | var hostname string
16 |
17 | func init() {
18 | hostname, _ = os.Hostname()
19 |
20 | switch config.EnvCfg.LoggerLevel {
21 | case "DEBUG":
22 | log.SetLevel(log.DebugLevel)
23 | case "INFO":
24 | log.SetLevel(log.InfoLevel)
25 | case "WARN", "WARNING":
26 | log.SetLevel(log.WarnLevel)
27 | case "ERROR":
28 | log.SetLevel(log.ErrorLevel)
29 | case "FATAL":
30 | log.SetLevel(log.FatalLevel)
31 | case "TRACE":
32 | log.SetLevel(log.TraceLevel)
33 | }
34 |
35 | filePath := path.Join("/var", "log", "gugotik", "gugotik.log")
36 | dir := path.Dir(filePath)
37 | if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
38 | panic(err)
39 | }
40 |
41 | f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
42 | if err != nil {
43 | panic(err)
44 | }
45 |
46 | log.SetFormatter(&log.JSONFormatter{})
47 | log.AddHook(logTraceHook{})
48 | log.SetOutput(io.MultiWriter(f, os.Stdout))
49 |
50 | Logger = log.WithFields(log.Fields{
51 | "Tied": config.EnvCfg.TiedLogging,
52 | "Hostname": hostname,
53 | "PodIP": config.EnvCfg.PodIpAddr,
54 | })
55 | }
56 |
57 | type logTraceHook struct{}
58 |
59 | func (t logTraceHook) Levels() []log.Level { return log.AllLevels }
60 |
61 | func (t logTraceHook) Fire(entry *log.Entry) error {
62 | ctx := entry.Context
63 | if ctx == nil {
64 | return nil
65 | }
66 |
67 | span := trace.SpanFromContext(ctx)
68 | //if !span.IsRecording() {
69 | // return nil
70 | //}
71 |
72 | sCtx := span.SpanContext()
73 | if sCtx.HasTraceID() {
74 | entry.Data["trace_id"] = sCtx.TraceID().String()
75 | }
76 | if sCtx.HasSpanID() {
77 | entry.Data["span_id"] = sCtx.SpanID().String()
78 | }
79 |
80 | if config.EnvCfg.LoggerWithTraceState == "enable" {
81 | attrs := make([]attribute.KeyValue, 0)
82 | logSeverityKey := attribute.Key("log.severity")
83 | logMessageKey := attribute.Key("log.message")
84 | attrs = append(attrs, logSeverityKey.String(entry.Level.String()))
85 | attrs = append(attrs, logMessageKey.String(entry.Message))
86 | for key, value := range entry.Data {
87 | fields := attribute.Key(fmt.Sprintf("log.fields.%s", key))
88 | attrs = append(attrs, fields.String(fmt.Sprintf("%v", value)))
89 | }
90 | span.AddEvent("log", trace.WithAttributes(attrs...))
91 | if entry.Level <= log.ErrorLevel {
92 | span.SetStatus(codes.Error, entry.Message)
93 | }
94 | }
95 | return nil
96 | }
97 |
98 | var Logger *log.Entry
99 |
100 | func LogService(name string) *log.Entry {
101 | return Logger.WithFields(log.Fields{
102 | "Service": name,
103 | })
104 | }
105 |
106 | func SetSpanError(span trace.Span, err error) {
107 | span.RecordError(err)
108 | span.SetStatus(codes.Error, "Internal Error")
109 | }
110 |
111 | func SetSpanErrorWithDesc(span trace.Span, err error, desc string) {
112 | span.RecordError(err)
113 | span.SetStatus(codes.Error, desc)
114 | }
115 |
116 | func SetSpanWithHostname(span trace.Span) {
117 | span.SetAttributes(attribute.String("hostname", hostname))
118 | span.SetAttributes(attribute.String("podIP", config.EnvCfg.PodIpAddr))
119 | }
120 |
--------------------------------------------------------------------------------
/src/utils/pathgen/video.go:
--------------------------------------------------------------------------------
1 | package pathgen
2 |
3 | import (
4 | "crypto/sha256"
5 | "encoding/hex"
6 | "strconv"
7 | )
8 |
9 | // GenerateRawVideoName 生成初始视频名称,此链接仅用于内部使用,暴露给用户的视频名称
10 | func GenerateRawVideoName(actorId uint32, title string, videoId uint32) string {
11 | hash := sha256.Sum256([]byte("RAW" + strconv.FormatUint(uint64(actorId), 10) + title + strconv.FormatUint(uint64(videoId), 10)))
12 | return hex.EncodeToString(hash[:]) + ".mp4"
13 | }
14 |
15 | // GenerateFinalVideoName 最终暴露给用户的视频名称
16 | func GenerateFinalVideoName(actorId uint32, title string, videoId uint32) string {
17 | hash := sha256.Sum256([]byte(strconv.FormatUint(uint64(actorId), 10) + title + strconv.FormatUint(uint64(videoId), 10)))
18 | return hex.EncodeToString(hash[:]) + ".mp4"
19 | }
20 |
21 | // GenerateCoverName 生成视频封面名称
22 | func GenerateCoverName(actorId uint32, title string, videoId uint32) string {
23 | hash := sha256.Sum256([]byte(strconv.FormatUint(uint64(actorId), 10) + title + strconv.FormatUint(uint64(videoId), 10)))
24 | return hex.EncodeToString(hash[:]) + ".png"
25 | }
26 |
27 | // GenerateAudioName 生成音频链接,此链接仅用于内部使用,不暴露给用户
28 | func GenerateAudioName(videoFileName string) string {
29 | hash := sha256.Sum256([]byte("AUDIO_" + videoFileName))
30 | return hex.EncodeToString(hash[:]) + ".mp3"
31 | }
32 |
33 | // GenerateNameWatermark 生成用户名水印图片
34 | func GenerateNameWatermark(actorId uint32, Name string) string {
35 | hash := sha256.Sum256([]byte("Watermark" + strconv.FormatUint(uint64(actorId), 10) + Name))
36 | return hex.EncodeToString(hash[:]) + ".png"
37 | }
38 |
--------------------------------------------------------------------------------
/src/utils/prom/interceptor.go:
--------------------------------------------------------------------------------
1 | package prom
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "context"
6 | "github.com/prometheus/client_golang/prometheus"
7 | "go.opentelemetry.io/otel/trace"
8 | )
9 |
10 | func ExtractContext(ctx context.Context) prometheus.Labels {
11 | if span := trace.SpanContextFromContext(ctx); span.IsSampled() {
12 | return prometheus.Labels{
13 | "traceID": span.TraceID().String(),
14 | "spanID": span.SpanID().String(),
15 | "podId": config.EnvCfg.PodIpAddr,
16 | }
17 | }
18 | return nil
19 | }
20 |
--------------------------------------------------------------------------------
/src/utils/prom/pack.go:
--------------------------------------------------------------------------------
1 | package prom
2 |
3 | import "github.com/prometheus/client_golang/prometheus"
4 |
5 | var Client = prometheus.NewRegistry()
6 |
--------------------------------------------------------------------------------
/src/utils/ptr/ptr.go:
--------------------------------------------------------------------------------
1 | package ptr
2 |
3 | func Ptr[T any](a T) *T {
4 | return &a
5 | }
6 |
--------------------------------------------------------------------------------
/src/utils/rabbitmq/mq.go:
--------------------------------------------------------------------------------
1 | package rabbitmq
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "fmt"
6 | )
7 |
8 | func BuildMQConnAddr() string {
9 | return fmt.Sprintf("amqp://%s:%s@%s:%s/%s", config.EnvCfg.RabbitMQUsername, config.EnvCfg.RabbitMQPassword,
10 | config.EnvCfg.RabbitMQAddr, config.EnvCfg.RabbitMQPort, config.EnvCfg.RabbitMQVhostPrefix)
11 | }
12 |
--------------------------------------------------------------------------------
/src/utils/rabbitmq/otel.go:
--------------------------------------------------------------------------------
1 | package rabbitmq
2 |
3 | import (
4 | "context"
5 | "go.opentelemetry.io/otel"
6 | )
7 |
8 | type AmqpHeadersCarrier map[string]interface{}
9 |
10 | func (a AmqpHeadersCarrier) Get(key string) string {
11 | v, ok := a[key]
12 | if !ok {
13 | return ""
14 | }
15 | return v.(string)
16 | }
17 |
18 | func (a AmqpHeadersCarrier) Set(key string, value string) {
19 | a[key] = value
20 | }
21 |
22 | func (a AmqpHeadersCarrier) Keys() []string {
23 | i := 0
24 | r := make([]string, len(a))
25 |
26 | for k := range a {
27 | r[i] = k
28 | i++
29 | }
30 |
31 | return r
32 | }
33 |
34 | func InjectAMQPHeaders(ctx context.Context) map[string]interface{} {
35 | h := make(AmqpHeadersCarrier)
36 | otel.GetTextMapPropagator().Inject(ctx, h)
37 | return h
38 | }
39 |
40 | func ExtractAMQPHeaders(ctx context.Context, headers map[string]interface{}) context.Context {
41 | return otel.GetTextMapPropagator().Extract(ctx, AmqpHeadersCarrier(headers))
42 | }
43 |
--------------------------------------------------------------------------------
/src/web/about/handler.go:
--------------------------------------------------------------------------------
1 | package about
2 |
3 | import (
4 | "GuGoTik/src/constant/strings"
5 | "GuGoTik/src/extra/tracing"
6 | "GuGoTik/src/utils/logging"
7 | "GuGoTik/src/web/models"
8 | "github.com/gin-gonic/gin"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | func Handle(c *gin.Context) {
14 | _, span := tracing.Tracer.Start(c.Request.Context(), "AboutHandler")
15 | defer span.End()
16 | logging.SetSpanWithHostname(span)
17 |
18 | var req models.AboutReq
19 | if err := c.ShouldBind(&req); err != nil {
20 | c.JSON(http.StatusBadRequest, gin.H{
21 | "status_code": strings.GateWayErrorCode,
22 | "status_msg": strings.GateWayError,
23 | })
24 | }
25 | res := models.AboutRes{
26 | Echo: req.Echo,
27 | TimeStamp: time.Now().Unix(),
28 | }
29 | c.JSON(http.StatusOK, res)
30 | }
31 |
--------------------------------------------------------------------------------
/src/web/auth/handler.go:
--------------------------------------------------------------------------------
1 | package auth
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/constant/strings"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/auth"
8 | grpc2 "GuGoTik/src/utils/grpc"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/web/models"
11 | "GuGoTik/src/web/utils"
12 | "github.com/gin-gonic/gin"
13 | _ "github.com/mbobakov/grpc-consul-resolver"
14 | "github.com/sirupsen/logrus"
15 | "net/http"
16 | )
17 |
18 | var Client auth.AuthServiceClient
19 |
20 | func LoginHandle(c *gin.Context) {
21 | var req models.LoginReq
22 | _, span := tracing.Tracer.Start(c.Request.Context(), "LoginHandler")
23 | defer span.End()
24 | logging.SetSpanWithHostname(span)
25 | logger := logging.LogService("GateWay.Login").WithContext(c.Request.Context())
26 |
27 | if err := c.ShouldBindQuery(&req); err != nil {
28 | c.JSON(http.StatusOK, models.LoginRes{
29 | StatusCode: strings.GateWayParamsErrorCode,
30 | StatusMsg: strings.GateWayParamsError,
31 | UserId: 0,
32 | Token: "",
33 | })
34 | return
35 | }
36 |
37 | res, err := Client.Login(c.Request.Context(), &auth.LoginRequest{
38 | Username: req.UserName,
39 | Password: req.Password,
40 | })
41 | if err != nil {
42 | logger.WithFields(logrus.Fields{
43 | "Username": req.UserName,
44 | }).Warnf("Error when trying to connect with AuthService")
45 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
46 | return
47 | }
48 |
49 | logger.WithFields(logrus.Fields{
50 | "Username": req.UserName,
51 | "Token": res.Token,
52 | "UserId": res.UserId,
53 | }).Infof("User log in")
54 |
55 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
56 | }
57 |
58 | func RegisterHandle(c *gin.Context) {
59 | var req models.RegisterReq
60 | _, span := tracing.Tracer.Start(c.Request.Context(), "LoginHandler")
61 | defer span.End()
62 | logger := logging.LogService("GateWay.Register").WithContext(c.Request.Context())
63 |
64 | if err := c.ShouldBindQuery(&req); err != nil {
65 | c.JSON(http.StatusOK, models.RegisterRes{
66 | StatusCode: strings.GateWayParamsErrorCode,
67 | StatusMsg: strings.GateWayParamsError,
68 | UserId: 0,
69 | Token: "",
70 | })
71 | return
72 | }
73 |
74 | res, err := Client.Register(c.Request.Context(), &auth.RegisterRequest{
75 | Username: req.UserName,
76 | Password: req.Password,
77 | })
78 |
79 | if err != nil {
80 | logger.WithFields(logrus.Fields{
81 | "Username": req.UserName,
82 | }).Warnf("Error when trying to connect with AuthService")
83 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
84 | return
85 | }
86 |
87 | logger.WithFields(logrus.Fields{
88 | "Username": req.UserName,
89 | "Token": res.Token,
90 | "UserId": res.UserId,
91 | }).Infof("User register in")
92 |
93 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
94 | }
95 |
96 | func init() {
97 | conn := grpc2.Connect(config.AuthRpcServerName)
98 | Client = auth.NewAuthServiceClient(conn)
99 | }
100 |
--------------------------------------------------------------------------------
/src/web/favorite/handler.go:
--------------------------------------------------------------------------------
1 | package favorite
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/constant/strings"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/favorite"
8 | grpc2 "GuGoTik/src/utils/grpc"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/web/models"
11 | "GuGoTik/src/web/utils"
12 | "github.com/gin-gonic/gin"
13 | "github.com/sirupsen/logrus"
14 | "net/http"
15 | )
16 |
17 | var Client favorite.FavoriteServiceClient
18 |
19 | func init() {
20 | conn := grpc2.Connect(config.FavoriteRpcServerName)
21 | Client = favorite.NewFavoriteServiceClient(conn)
22 | }
23 |
24 | func ActionFavoriteHandler(c *gin.Context) {
25 | var req models.ActionFavoriteReq
26 | _, span := tracing.Tracer.Start(c.Request.Context(), "ActionFavoriteHandler")
27 | defer span.End()
28 | logging.SetSpanWithHostname(span)
29 | logger := logging.LogService("GateWay.ActionFavorite").WithContext(c.Request.Context())
30 |
31 | if err := c.ShouldBindQuery(&req); err != nil {
32 | c.JSON(http.StatusOK, models.ActionCommentRes{
33 | StatusCode: strings.GateWayParamsErrorCode,
34 | StatusMsg: strings.GateWayParamsError,
35 | })
36 | return
37 | }
38 |
39 | actionType := uint32(req.ActionType)
40 | if actionType != uint32(1) && actionType != uint32(2) {
41 | c.JSON(http.StatusOK, models.ActionCommentRes{
42 | StatusCode: strings.GateWayParamsErrorCode,
43 | StatusMsg: strings.GateWayParamsError,
44 | })
45 | return
46 | }
47 | res, err := Client.FavoriteAction(c.Request.Context(), &favorite.FavoriteRequest{
48 | ActorId: uint32(req.ActorId),
49 | VideoId: uint32(req.VideoId),
50 | ActionType: actionType,
51 | })
52 |
53 | if err != nil {
54 | logger.WithFields(logrus.Fields{
55 | "ActorId": req.ActorId,
56 | "VideoId": req.VideoId,
57 | "ActionType": req.ActionType,
58 | }).Warnf("Error when trying to connect with ActionFavoriteService")
59 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
60 | return
61 | }
62 |
63 | logger.WithFields(logrus.Fields{
64 | "ActorId": req.ActorId,
65 | "VideoId": req.VideoId,
66 | "ActionType": req.ActionType,
67 | }).Infof("Action favorite success")
68 |
69 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
70 | }
71 |
72 | func ListFavoriteHandler(c *gin.Context) {
73 | var req models.ListFavoriteReq
74 | _, span := tracing.Tracer.Start(c.Request.Context(), "ListFavoriteHandler")
75 | defer span.End()
76 | logger := logging.LogService("GateWay.ListFavorite").WithContext(c.Request.Context())
77 |
78 | if err := c.ShouldBindQuery(&req); err != nil {
79 | c.JSON(http.StatusOK, models.ListCommentRes{
80 | StatusCode: strings.GateWayParamsErrorCode,
81 | StatusMsg: strings.GateWayParamsError,
82 | })
83 | return
84 | }
85 |
86 | res, err := Client.FavoriteList(c.Request.Context(), &favorite.FavoriteListRequest{
87 | ActorId: uint32(req.ActorId),
88 | UserId: uint32(req.UserId),
89 | })
90 | if err != nil {
91 | logger.WithFields(logrus.Fields{
92 | "ActorId": req.ActorId,
93 | "UserId": req.UserId,
94 | }).Warnf("Error when trying to connect with ListFavoriteHandler")
95 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
96 | return
97 | }
98 |
99 | logger.WithFields(logrus.Fields{
100 | "ActorId": req.ActorId,
101 | "UserId": req.UserId,
102 | }).Infof("List favorite videos success")
103 |
104 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
105 | }
106 |
--------------------------------------------------------------------------------
/src/web/feed/handler.go:
--------------------------------------------------------------------------------
1 | package feed
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/constant/strings"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/feed"
8 | grpc2 "GuGoTik/src/utils/grpc"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/web/models"
11 | "GuGoTik/src/web/utils"
12 | "github.com/gin-gonic/gin"
13 | _ "github.com/mbobakov/grpc-consul-resolver"
14 | "github.com/sirupsen/logrus"
15 | "net/http"
16 | "strconv"
17 | )
18 |
19 | var Client feed.FeedServiceClient
20 |
21 | func ListVideosByRecommendHandle(c *gin.Context) {
22 | var req models.ListVideosReq
23 | _, span := tracing.Tracer.Start(c.Request.Context(), "Feed-ListVideosByRecommendHandle")
24 | defer span.End()
25 | logging.SetSpanWithHostname(span)
26 | logger := logging.LogService("GateWay.Videos").WithContext(c.Request.Context())
27 |
28 | if err := c.ShouldBindQuery(&req); err != nil {
29 | logger.WithFields(logrus.Fields{
30 | "latestTime": req.LatestTime,
31 | "err": err,
32 | }).Warnf("Error when trying to bind query")
33 | c.JSON(http.StatusOK, models.ListVideosRes{
34 | StatusCode: strings.GateWayParamsErrorCode,
35 | StatusMsg: strings.GateWayParamsError,
36 | NextTime: nil,
37 | VideoList: nil,
38 | })
39 | return
40 | }
41 |
42 | latestTime := req.LatestTime
43 | actorId := uint32(req.ActorId)
44 | var res *feed.ListFeedResponse
45 | var err error
46 | anonymity, err := strconv.ParseUint(config.EnvCfg.AnonymityUser, 10, 32)
47 | if err != nil {
48 | c.JSON(http.StatusOK, models.ListVideosRes{
49 | StatusCode: strings.FeedServiceInnerErrorCode,
50 | StatusMsg: strings.FeedServiceInnerError,
51 | NextTime: nil,
52 | VideoList: nil,
53 | })
54 | return
55 | }
56 | if actorId == uint32(anonymity) {
57 | res, err = Client.ListVideos(c.Request.Context(), &feed.ListFeedRequest{
58 | LatestTime: &latestTime,
59 | ActorId: &actorId,
60 | })
61 | } else {
62 | res, err = Client.ListVideosByRecommend(c.Request.Context(), &feed.ListFeedRequest{
63 | LatestTime: &latestTime,
64 | ActorId: &actorId,
65 | })
66 | }
67 | if err != nil {
68 | logger.WithFields(logrus.Fields{
69 | "LatestTime": latestTime,
70 | "Err": err,
71 | }).Warnf("Error when trying to connect with FeedService")
72 | c.JSON(http.StatusOK, models.ListVideosRes{
73 | StatusCode: strings.FeedServiceInnerErrorCode,
74 | StatusMsg: strings.FeedServiceInnerError,
75 | NextTime: nil,
76 | VideoList: nil,
77 | })
78 | return
79 | }
80 | c.Render(http.StatusOK, utils.CustomJSON{Data: res, Context: c})
81 | }
82 |
83 | func init() {
84 | conn := grpc2.Connect(config.FeedRpcServerName)
85 | Client = feed.NewFeedServiceClient(conn)
86 | }
87 |
--------------------------------------------------------------------------------
/src/web/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/extra/profiling"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/utils/logging"
8 | "GuGoTik/src/web/about"
9 | "GuGoTik/src/web/auth"
10 | comment2 "GuGoTik/src/web/comment"
11 | favorite2 "GuGoTik/src/web/favorite"
12 | feed2 "GuGoTik/src/web/feed"
13 | message2 "GuGoTik/src/web/message"
14 | "GuGoTik/src/web/middleware"
15 | publish2 "GuGoTik/src/web/publish"
16 | relation2 "GuGoTik/src/web/relation"
17 | user2 "GuGoTik/src/web/user"
18 | "context"
19 | "github.com/gin-contrib/gzip"
20 | "github.com/gin-gonic/gin"
21 | "github.com/sirupsen/logrus"
22 | ginprometheus "github.com/zsais/go-gin-prometheus"
23 | "go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
24 | "time"
25 | )
26 |
27 | func main() {
28 | // Set Trace Provider
29 | tp, err := tracing.SetTraceProvider(config.WebServiceName)
30 |
31 | if err != nil {
32 | logging.Logger.WithFields(logrus.Fields{
33 | "err": err,
34 | }).Panicf("Error to set the trace")
35 | }
36 | defer func() {
37 | if err := tp.Shutdown(context.Background()); err != nil {
38 | logging.Logger.WithFields(logrus.Fields{
39 | "err": err,
40 | }).Errorf("Error to set the trace")
41 | }
42 | }()
43 |
44 | g := gin.Default()
45 | // Configure Prometheus
46 | p := ginprometheus.NewPrometheus("GuGoTik-WebGateway")
47 | p.Use(g)
48 | // Configure Gzip
49 | g.Use(gzip.Gzip(gzip.DefaultCompression))
50 | // Configure Tracing
51 | g.Use(otelgin.Middleware(config.WebServiceName))
52 | g.Use(middleware.TokenAuthMiddleware())
53 | g.Use(middleware.RateLimiterMiddleWare(time.Second, 1000, 1000))
54 |
55 | // Configure Pyroscope
56 | profiling.InitPyroscope("GuGoTik.GateWay")
57 |
58 | // Register Service
59 | // Test Service
60 | g.GET("/about", about.Handle)
61 | // Production Service
62 | rootPath := g.Group("/douyin")
63 | user := rootPath.Group("/user")
64 | {
65 | user.GET("/", user2.UserHandler)
66 | user.POST("/login/", auth.LoginHandle)
67 | user.POST("/register/", auth.RegisterHandle)
68 | }
69 | feed := rootPath.Group("/feed")
70 | {
71 | feed.GET("/", feed2.ListVideosByRecommendHandle)
72 | }
73 | comment := rootPath.Group("/comment")
74 | {
75 | comment.POST("/action/", comment2.ActionCommentHandler)
76 | comment.GET("/list/", comment2.ListCommentHandler)
77 | comment.GET("/count/", comment2.CountCommentHandler)
78 | }
79 | relation := rootPath.Group("/relation")
80 | {
81 | //todo: frontend
82 | relation.POST("/action/", relation2.ActionRelationHandler)
83 | relation.POST("/follow/", relation2.FollowHandler)
84 | relation.POST("/unfollow/", relation2.UnfollowHandler)
85 | relation.GET("/follow/list/", relation2.GetFollowListHandler)
86 | relation.GET("/follower/list/", relation2.GetFollowerListHandler)
87 | relation.GET("/friend/list/", relation2.GetFriendListHandler)
88 | relation.GET("/follow/count/", relation2.CountFollowHandler)
89 | relation.GET("/follower/count/", relation2.CountFollowerHandler)
90 | relation.GET("/isFollow/", relation2.IsFollowHandler)
91 | }
92 |
93 | publish := rootPath.Group("/publish")
94 | {
95 | publish.POST("/action/", publish2.ActionPublishHandle)
96 | publish.GET("/list/", publish2.ListPublishHandle)
97 | }
98 | //todo
99 | message := rootPath.Group("/message")
100 | {
101 | message.GET("/chat/", message2.ListMessageHandler)
102 | message.POST("/action/", message2.ActionMessageHandler)
103 | }
104 | favorite := rootPath.Group("/favorite")
105 | {
106 | favorite.POST("/action/", favorite2.ActionFavoriteHandler)
107 | favorite.GET("/list/", favorite2.ListFavoriteHandler)
108 | }
109 | // Run Server
110 | if err := g.Run(config.WebServiceAddr); err != nil {
111 | panic("Can not run GuGoTik Gateway, binding port: " + config.WebServiceAddr)
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/web/middleware/authenticate.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/constant/strings"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/auth"
8 | grpc2 "GuGoTik/src/utils/grpc"
9 | "GuGoTik/src/utils/logging"
10 | "github.com/gin-gonic/gin"
11 | "github.com/sirupsen/logrus"
12 | "go.opentelemetry.io/otel/attribute"
13 | "net/http"
14 | "strconv"
15 | )
16 |
17 | var client auth.AuthServiceClient
18 |
19 | func TokenAuthMiddleware() gin.HandlerFunc {
20 | return func(c *gin.Context) {
21 | ctx, span := tracing.Tracer.Start(c.Request.Context(), "AuthMiddleWare")
22 | defer span.End()
23 | logging.SetSpanWithHostname(span)
24 | logger := logging.LogService("GateWay.AuthMiddleWare").WithContext(ctx)
25 | span.SetAttributes(attribute.String("url", c.Request.URL.Path))
26 | if c.Request.URL.Path == "/douyin/user/login/" ||
27 | c.Request.URL.Path == "/douyin/user/register/" ||
28 | c.Request.URL.Path == "/douyin/comment/list/" ||
29 | c.Request.URL.Path == "/douyin/publish/list/" ||
30 | c.Request.URL.Path == "/douyin/favorite/list/" {
31 | c.Request.URL.RawQuery += "&actor_id=" + config.EnvCfg.AnonymityUser
32 | span.SetAttributes(attribute.String("mark_url", c.Request.URL.String()))
33 | logger.WithFields(logrus.Fields{
34 | "Path": c.Request.URL.Path,
35 | }).Debugf("Skip Auth with targeted url")
36 | c.Next()
37 | return
38 | }
39 |
40 | var token string
41 | if c.Request.URL.Path == "/douyin/publish/action/" {
42 | token = c.PostForm("token")
43 | } else {
44 | token = c.Query("token")
45 | }
46 |
47 | if token == "" && (c.Request.URL.Path == "/douyin/feed/" ||
48 | c.Request.URL.Path == "/douyin/relation/follow/list/" ||
49 | c.Request.URL.Path == "/douyin/relation/follower/list/") {
50 | c.Request.URL.RawQuery += "&actor_id=" + config.EnvCfg.AnonymityUser
51 | span.SetAttributes(attribute.String("mark_url", c.Request.URL.String()))
52 | logger.WithFields(logrus.Fields{
53 | "Path": c.Request.URL.Path,
54 | }).Debugf("Skip Auth with targeted url")
55 | c.Next()
56 | return
57 | }
58 | span.SetAttributes(attribute.String("token", token))
59 | // Verify User Token
60 | authenticate, err := client.Authenticate(c.Request.Context(), &auth.AuthenticateRequest{Token: token})
61 | if err != nil {
62 | logger.WithFields(logrus.Fields{
63 | "err": err,
64 | }).Errorf("Gateway Auth meet trouble")
65 | span.RecordError(err)
66 | c.JSON(http.StatusOK, gin.H{
67 | "status_code": strings.GateWayErrorCode,
68 | "status_msg": strings.GateWayError,
69 | })
70 | c.Abort()
71 | return
72 | }
73 |
74 | if authenticate.StatusCode != 0 {
75 | c.JSON(http.StatusUnauthorized, gin.H{
76 | "status_code": strings.AuthUserNeededCode,
77 | "status_msg": strings.AuthUserNeeded,
78 | })
79 | c.Abort()
80 | return
81 | }
82 |
83 | c.Request.URL.RawQuery += "&actor_id=" + strconv.FormatUint(uint64(authenticate.UserId), 10)
84 | c.Next()
85 | }
86 | }
87 |
88 | func init() {
89 | authConn := grpc2.Connect(config.AuthRpcServerName)
90 | client = auth.NewAuthServiceClient(authConn)
91 | }
92 |
--------------------------------------------------------------------------------
/src/web/middleware/limiter.go:
--------------------------------------------------------------------------------
1 | package middleware
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/juju/ratelimit"
6 | "net/http"
7 | "time"
8 | )
9 |
10 | func RateLimiterMiddleWare(fillInterval time.Duration, cap, quantum int64) gin.HandlerFunc {
11 | bucket := ratelimit.NewBucketWithQuantum(fillInterval, cap, quantum)
12 | return func(c *gin.Context) {
13 | if bucket.TakeAvailable(1) < 1 {
14 | c.String(http.StatusForbidden, "rate limit...")
15 | c.Abort()
16 | return
17 | }
18 | c.Next()
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/web/models/About.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type AboutReq struct {
4 | Echo string `json:"echo" uri:"echo" form:"echo"`
5 | }
6 |
7 | type AboutRes struct {
8 | TimeStamp int64 `json:"time_stamp"`
9 | Echo string `json:"echo,omitempty"`
10 | }
11 |
--------------------------------------------------------------------------------
/src/web/models/Comment.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "GuGoTik/src/rpc/comment"
4 |
5 | type ActionCommentReq struct {
6 | Token string `form:"token" binding:"required"`
7 | ActorId int `form:"actor_id"`
8 | VideoId int `form:"video_id" binding:"-"`
9 | ActionType int `form:"action_type" binding:"required"`
10 | CommentText string `form:"comment_text"`
11 | CommentId int `form:"comment_id"`
12 | }
13 |
14 | type ActionCommentRes struct {
15 | StatusCode int `json:"status_code"`
16 | StatusMsg string `json:"status_msg"`
17 | Comment comment.Comment `json:"comment"`
18 | }
19 |
20 | type ListCommentReq struct {
21 | Token string `form:"token"`
22 | ActorId int `form:"actor_id"`
23 | VideoId int `form:"video_id" binding:"-"`
24 | }
25 |
26 | type ListCommentRes struct {
27 | StatusCode int `json:"status_code"`
28 | StatusMsg string `json:"status_msg"`
29 | CommentList []*comment.Comment `json:"comment_list"`
30 | }
31 |
32 | type CountCommentReq struct {
33 | Token string `form:"token"`
34 | ActorId int `form:"actor_id"`
35 | VideoId int `form:"video_id" binding:"-"`
36 | }
37 |
38 | type CountCommentRes struct {
39 | StatusCode int `json:"status_code"`
40 | StatusMsg string `json:"status_msg"`
41 | CommentCount int `json:"comment_count"`
42 | }
43 |
--------------------------------------------------------------------------------
/src/web/models/Favorite.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/rpc/feed"
5 | )
6 |
7 | type ActionFavoriteReq struct {
8 | Token string `form:"token" binding:"required"`
9 | ActorId int `form:"actor_id" binding:"required"`
10 | VideoId int `form:"video_id" binding:"required"`
11 | ActionType int `form:"action_type" binding:"required"`
12 | }
13 |
14 | type ActionFavoriteRes struct {
15 | StatusCode int `json:"status_code"`
16 | StatusMsg string `json:"status_msg"`
17 | }
18 |
19 | type ListFavoriteReq struct {
20 | Token string `form:"token"`
21 | ActorId int `form:"actor_id"`
22 | UserId int `form:"user_id" binding:"required"`
23 | }
24 |
25 | type ListFavoriteRes struct {
26 | StatusCode int `json:"status_code"`
27 | StatusMsg string `json:"status_msg"`
28 | VideoList []*feed.Video `json:"video_list"`
29 | }
30 |
--------------------------------------------------------------------------------
/src/web/models/Feed.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/rpc/feed"
5 | )
6 |
7 | type ListVideosReq struct {
8 | LatestTime string `form:"latest_time"`
9 | ActorId int `form:"actor_id"`
10 | }
11 |
12 | type ListVideosRes struct {
13 | StatusCode int `json:"status_code"`
14 | StatusMsg string `json:"status_msg"`
15 | NextTime *int64 `json:"next_time,omitempty"`
16 | VideoList []*feed.Video `json:"video_list,omitempty"`
17 | }
18 |
--------------------------------------------------------------------------------
/src/web/models/Login.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type LoginReq struct {
4 | UserName string `form:"username" binding:"required"`
5 | Password string `form:"password" binding:"required"`
6 | }
7 |
8 | type LoginRes struct {
9 | StatusCode int `json:"status_code"`
10 | StatusMsg string `json:"status_msg"`
11 | UserId uint32 `json:"user_id"`
12 | Token string `json:"token"`
13 | }
14 |
15 | type RegisterReq struct {
16 | UserName string `form:"username" binding:"required"`
17 | Password string `form:"password" binding:"required"`
18 | }
19 |
20 | type RegisterRes struct {
21 | StatusCode int `json:"status_code"`
22 | StatusMsg string `json:"status_msg"`
23 | UserId int `json:"user_id"`
24 | Token string `json:"token"`
25 | }
26 |
--------------------------------------------------------------------------------
/src/web/models/Message.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import (
4 | "GuGoTik/src/rpc/chat"
5 | )
6 |
7 | // SMessageReq 这个是发数据的数据结构
8 | type SMessageReq struct {
9 | ActorId int `form:"actor_id" binding:"required"`
10 | ToUserId int `form:"to_user_id" binding:"required"`
11 | Content string `form:"content" binding:"required"`
12 | ActionType int `form:"action_type" binding:"required"` // send message
13 | //Create_time string //time maybe have some question
14 | }
15 |
16 | // SMessageRes 收的状态
17 | // status_code 状态码 0- 成功 其他值 -失败
18 | // status_msg 返回状态描述
19 | type SMessageRes struct {
20 | StatusCode int `json:"status_code"`
21 | StatusMsg string `json:"status_msg"`
22 | }
23 |
24 | type ListMessageReq struct {
25 | ActorId uint32 `form:"actor_id" binding:"required"`
26 | ToUserId uint32 `form:"to_user_id" binding:"required"`
27 | PreMsgTime uint64 `form:"pre_msg_time"`
28 | }
29 |
30 | type ListMessageRes struct {
31 | StatusCode int `json:"status_code"`
32 | StatusMsg string `json:"status_msg"`
33 | MessageList []*chat.Message `json:"message_list"`
34 | }
35 |
--------------------------------------------------------------------------------
/src/web/models/Publish.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "GuGoTik/src/rpc/feed"
4 |
5 | type ListPublishReq struct {
6 | Token string `form:"token" binding:"required"`
7 | ActorId uint32 `form:"actor_id" binding:"required"`
8 | UserId uint32 `form:"user_id" binding:"required"`
9 | }
10 |
11 | type ListPublishRes struct {
12 | StatusCode int `json:"status_code"`
13 | StatusMsg string `json:"status_msg"`
14 | VideoList []*feed.Video `json:"video_list"`
15 | }
16 |
17 | type ActionPublishReq struct {
18 | ActorId uint32 `form:"actor_id" binding:"required"`
19 | }
20 |
21 | type ActionPublishRes struct {
22 | StatusCode int `json:"status_code"`
23 | StatusMsg string `json:"status_msg"`
24 | }
25 |
--------------------------------------------------------------------------------
/src/web/models/Relation.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import "GuGoTik/src/rpc/user"
4 |
5 | //
6 | //type RelationActionReq struct {
7 | // Token string `form:"token" binding:"required"`
8 | // ActorId int `form:"actor_id"`
9 | // UserId int `form:"user_id"`
10 | //}
11 |
12 | type RelationActionReq struct {
13 | Token string `form:"token" binding:"required"`
14 | ActorId int `form:"actor_id"`
15 | UserId int `form:"to_user_id"`
16 | ActionType int `form:"action_type" binding:"required"`
17 | }
18 |
19 | type RelationActionRes struct {
20 | StatusCode int `json:"status_code"`
21 | StatusMsg string `json:"status_msg"`
22 | }
23 |
24 | type FollowListReq struct {
25 | Token string `form:"token"`
26 | ActorId int `form:"actor_id"`
27 | UserId int `form:"user_id"`
28 | }
29 |
30 | type FollowListRes struct {
31 | StatusCode int `json:"status_code"`
32 | StatusMsg string `json:"status_msg"`
33 | UserList []*user.User `json:"user_list"`
34 | }
35 |
36 | type CountFollowListReq struct {
37 | Token string `form:"token" binding:"required"`
38 | UserId int `form:"user_id"`
39 | }
40 |
41 | type CountFollowListRes struct {
42 | StatusCode int `json:"status_code"`
43 | StatusMsg string `json:"status_msg"`
44 | Count int `json:"count"`
45 | }
46 |
47 | type FollowerListReq struct {
48 | Token string `form:"token"`
49 | ActorId int `form:"actor_id"`
50 | UserId int `form:"user_id"`
51 | }
52 |
53 | type FollowerListRes struct {
54 | StatusCode int `json:"status_code"`
55 | StatusMsg string `json:"status_msg"`
56 | UserList []*user.User `json:"user_list"`
57 | }
58 |
59 | type CountFollowerListReq struct {
60 | Token string `form:"token"`
61 | UserId int `form:"user_id"`
62 | }
63 |
64 | type CountFollowerListRes struct {
65 | StatusCode int `json:"status_code"`
66 | StatusMsg string `json:"status_msg"`
67 | Count int `json:"count"`
68 | }
69 |
70 | type FriendListReq struct {
71 | Token string `form:"token"`
72 | ActorId int `form:"actor_id"`
73 | UserId int `form:"user_id"`
74 | }
75 |
76 | type FriendListRes struct {
77 | StatusCode int `json:"status_code"`
78 | StatusMsg string `json:"status_msg"`
79 | UserList []*user.User `json:"user_list"`
80 | }
81 |
82 | type IsFollowReq struct {
83 | Token string `form:"token" binding:"required"`
84 | ActorId int `form:"actor_id"`
85 | UserId int `form:"user_id"`
86 | }
87 |
88 | type IsFollowRes struct {
89 | Result bool `json:"result"`
90 | }
91 |
--------------------------------------------------------------------------------
/src/web/models/User.go:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | type UserReq struct {
4 | UserId uint32 `form:"user_id" binding:"required"`
5 | ActorId uint32 `form:"actor_id" binding:"required"`
6 | }
7 |
8 | type UserRes struct {
9 | StatusCode int32 `json:"status_code"` // 状态码,0-成功,其他值-失败
10 | StatusMsg string `json:"status_msg"` // 返回状态描述
11 | }
12 |
--------------------------------------------------------------------------------
/src/web/user/handler.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/constant/strings"
6 | "GuGoTik/src/extra/tracing"
7 | "GuGoTik/src/rpc/user"
8 | grpc2 "GuGoTik/src/utils/grpc"
9 | "GuGoTik/src/utils/logging"
10 | "GuGoTik/src/web/models"
11 | "GuGoTik/src/web/utils"
12 | "net/http"
13 |
14 | "github.com/gin-gonic/gin"
15 | "github.com/sirupsen/logrus"
16 | )
17 |
18 | var userClient user.UserServiceClient
19 |
20 | func init() {
21 | userConn := grpc2.Connect(config.UserRpcServerName)
22 | userClient = user.NewUserServiceClient(userConn)
23 | }
24 |
25 | func UserHandler(c *gin.Context) {
26 | var req models.UserReq
27 | _, span := tracing.Tracer.Start(c.Request.Context(), "UserInfoHandler")
28 | defer span.End()
29 | logging.SetSpanWithHostname(span)
30 | logger := logging.LogService("GateWay.UserInfo").WithContext(c.Request.Context())
31 |
32 | if err := c.ShouldBindQuery(&req); err != nil {
33 | c.JSON(http.StatusOK, models.UserRes{
34 | StatusCode: strings.GateWayParamsErrorCode,
35 | StatusMsg: strings.GateWayParamsError,
36 | })
37 | logging.SetSpanError(span, err)
38 | return
39 | }
40 |
41 | resp, err := userClient.GetUserInfo(c.Request.Context(), &user.UserRequest{
42 | UserId: req.UserId,
43 | ActorId: req.ActorId,
44 | })
45 |
46 | if err != nil {
47 | logger.WithFields(logrus.Fields{
48 | "err": err,
49 | }).Errorf("Error when gateway get info from UserInfo Service")
50 | logging.SetSpanError(span, err)
51 | c.Render(http.StatusOK, utils.CustomJSON{Data: resp, Context: c})
52 | return
53 | }
54 |
55 | c.Render(http.StatusOK, utils.CustomJSON{Data: resp, Context: c})
56 | }
57 |
--------------------------------------------------------------------------------
/src/web/utils/json.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "google.golang.org/protobuf/encoding/protojson"
6 | "google.golang.org/protobuf/proto"
7 | "net/http"
8 | )
9 |
10 | type CustomJSON struct {
11 | Data proto.Message
12 | Context *gin.Context
13 | }
14 |
15 | var m = protojson.MarshalOptions{
16 | EmitUnpopulated: true,
17 | UseProtoNames: true,
18 | }
19 |
20 | func (r CustomJSON) Render(w http.ResponseWriter) (err error) {
21 | r.WriteContentType(w)
22 | res, _ := m.Marshal(r.Data)
23 | _, err = w.Write(res)
24 | return
25 | }
26 |
27 | func (r CustomJSON) WriteContentType(w http.ResponseWriter) {
28 | w.Header().Set("Content-Type", "application/json; charset=utf-8")
29 | }
30 |
--------------------------------------------------------------------------------
/test/k6/comment.js:
--------------------------------------------------------------------------------
1 | import { sleep, check } from 'k6';
2 | import http from 'k6/http';
3 |
4 | export const options = {
5 | scenarios: {
6 | Scenario_1: {
7 | executor: 'ramping-vus',
8 | gracefulStop: '30s',
9 | stages: [
10 | { target: 1000, duration: '15s' },
11 | { target: 1500, duration: '30s' },
12 | { target: 1000, duration: '15s' },
13 | ],
14 | gracefulRampDown: '30s',
15 | exec: 'comment',
16 | },
17 | },
18 | }
19 |
20 | export function comment() {
21 | let res = http.post('http://127.0.0.1:37000/douyin/comment/action/?token=e75fae76-6a4e-4fa8-9b60-230e5d4f6b29&video_id=3048003698&action_type=1&comment_text=好好好')
22 |
23 | let jsonResponse = JSON.parse(res.body);
24 | check(jsonResponse, {
25 | 'status_code is 0': (json) => json.status_code === 0,
26 | });
27 | sleep(3)
28 | }
29 |
--------------------------------------------------------------------------------
/test/k6/comment_get.js:
--------------------------------------------------------------------------------
1 | import { sleep, check } from 'k6';
2 | import http from 'k6/http';
3 |
4 | export const options = {
5 | scenarios: {
6 | Scenario_1: {
7 | executor: 'ramping-vus',
8 | gracefulStop: '30s',
9 | stages: [
10 | { target: 1000, duration: '15s' },
11 | { target: 1500, duration: '30s' },
12 | { target: 1000, duration: '15s' },
13 | ],
14 | gracefulRampDown: '30s',
15 | exec: 'comment_get',
16 | },
17 | },
18 | }
19 |
20 | export function comment_get() {
21 | let res = http.get('http://127.0.0.1:37000/douyin/comment/list/?video_id=3048003698')
22 |
23 | let jsonResponse = JSON.parse(res.body);
24 | check(jsonResponse, {
25 | 'status_code is 0': (json) => json.status_code === 0,
26 | });
27 | sleep(3)
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/test/k6/favorite.js:
--------------------------------------------------------------------------------
1 | import { sleep, check } from 'k6';
2 | import http from 'k6/http';
3 |
4 | export const options = {
5 | scenarios: {
6 | Scenario_1: {
7 | executor: 'ramping-vus',
8 | gracefulStop: '30s',
9 | stages: [
10 | { target: 1000, duration: '15s' },
11 | { target: 1500, duration: '30s' },
12 | { target: 1000, duration: '15s' },
13 | ],
14 | gracefulRampDown: '30s',
15 | exec: 'favorite',
16 | },
17 | },
18 | }
19 |
20 | export function favorite() {
21 | let res = http.post('http://127.0.0.1:37000/douyin/favorite/action/?token=e75fae76-6a4e-4fa8-9b60-230e5d4f6b29&video_id=3048003698&action_type=1')
22 |
23 | let jsonResponse = JSON.parse(res.body);
24 | check(jsonResponse, {
25 | 'status_code is 0': (json) => json.status_code === 10008,
26 | });
27 | sleep(3)
28 | }
29 |
--------------------------------------------------------------------------------
/test/k6/favorite_random.js:
--------------------------------------------------------------------------------
1 | import { sleep } from 'k6';
2 | import http from 'k6/http';
3 |
4 | export const options = {
5 | scenarios: {
6 | Scenario_1: {
7 | executor: 'ramping-vus',
8 | gracefulStop: '30s',
9 | stages: [
10 | { target: 1000, duration: '15s' },
11 | { target: 1500, duration: '30s' },
12 | { target: 1000, duration: '15s' },
13 | ],
14 | gracefulRampDown: '30s',
15 | exec: 'favorite',
16 | },
17 | },
18 | }
19 |
20 | export function favorite() {
21 | let actionType = Math.random() < 0.5 ? 1 : 2;
22 | http.post(`http://127.0.0.1:37000/douyin/favorite/action/?token=e75fae76-6a4e-4fa8-9b60-230e5d4f6b29&video_id=3048003698&action_type=${actionType}`)
23 |
24 | sleep(3)
25 | }
26 |
--------------------------------------------------------------------------------
/test/k6/feed.js:
--------------------------------------------------------------------------------
1 | import { sleep, check } from 'k6';
2 | import http from 'k6/http';
3 |
4 | export const options = {
5 | scenarios: {
6 | feed: {
7 | executor: 'ramping-vus',
8 | startVUs: 0,
9 | gracefulStop: '30s',
10 | stages: [
11 | { target: 1000, duration: '20s' },
12 | { target: 2000, duration: '20s' },
13 | { target: 1000, duration: '20s' },
14 | ],
15 | gracefulRampDown: '30s',
16 | exec: 'feed',
17 | },
18 | },
19 | }
20 |
21 | export function feed() {
22 | let res = http.get('http://127.0.0.1:37000/douyin/feed?');
23 |
24 | let jsonResponse = JSON.parse(res.body);
25 | check(jsonResponse, {
26 | 'status_code is 0': (json) => json.status_code === 0,
27 | });
28 |
29 | sleep(3);
30 | }
31 |
--------------------------------------------------------------------------------
/test/rpc/authrpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/auth"
6 | "GuGoTik/src/rpc/health"
7 | "context"
8 | "fmt"
9 | "testing"
10 |
11 | "github.com/stretchr/testify/assert"
12 | "google.golang.org/grpc"
13 | "google.golang.org/grpc/credentials/insecure"
14 | )
15 |
16 | func TestHealth(t *testing.T) {
17 | var Client health.HealthClient
18 | req := health.HealthCheckRequest{}
19 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.AuthRpcServerPort),
20 | grpc.WithTransportCredentials(insecure.NewCredentials()),
21 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
22 | assert.Empty(t, err)
23 | Client = health.NewHealthClient(conn)
24 | check, err := Client.Check(context.Background(), &req)
25 | assert.Empty(t, err)
26 | assert.Equal(t, "SERVING", check.Status.String())
27 | }
28 |
29 | func TestRegister(t *testing.T) {
30 | var Client auth.AuthServiceClient
31 | req := auth.RegisterRequest{
32 | Username: "epicmo12312",
33 | Password: "epicmo12312312",
34 | }
35 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.AuthRpcServerPort),
36 | grpc.WithTransportCredentials(insecure.NewCredentials()),
37 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
38 | assert.Empty(t, err)
39 | Client = auth.NewAuthServiceClient(conn)
40 | res, err := Client.Register(context.Background(), &req)
41 | assert.Empty(t, err)
42 | assert.Equal(t, int32(0), res.StatusCode)
43 | }
44 |
45 | func TestLogin(t *testing.T) {
46 | var Client auth.AuthServiceClient
47 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.AuthRpcServerPort),
48 | grpc.WithTransportCredentials(insecure.NewCredentials()),
49 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
50 | assert.Empty(t, err)
51 | Client = auth.NewAuthServiceClient(conn)
52 | res, err := Client.Login(context.Background(), &auth.LoginRequest{
53 | Username: "epicmo",
54 | Password: "epicmo",
55 | })
56 | assert.Empty(t, err)
57 | assert.Equal(t, int32(0), res.StatusCode)
58 | }
59 |
--------------------------------------------------------------------------------
/test/rpc/commentrpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/comment"
6 | "context"
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/credentials/insecure"
11 | "os"
12 | "sync"
13 | "testing"
14 | )
15 |
16 | var Client comment.CommentServiceClient
17 |
18 | func setup() {
19 | conn, _ := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.CommentRpcServerPort),
20 | grpc.WithTransportCredentials(insecure.NewCredentials()),
21 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
22 |
23 | Client = comment.NewCommentServiceClient(conn)
24 | }
25 |
26 | func TestActionComment_Add(t *testing.T) {
27 | res, err := Client.ActionComment(context.Background(), &comment.ActionCommentRequest{
28 | ActorId: 1,
29 | VideoId: 0,
30 | ActionType: comment.ActionCommentType_ACTION_COMMENT_TYPE_ADD,
31 | Action: &comment.ActionCommentRequest_CommentText{CommentText: "I want to kill them all"},
32 | })
33 | assert.Empty(t, err)
34 | assert.Equal(t, int32(0), res.StatusCode)
35 | }
36 |
37 | func TestActionComment_Limiter(t *testing.T) {
38 | wg := &sync.WaitGroup{}
39 | for i := 0; i < 10; i++ {
40 | wg.Add(1)
41 | go func() {
42 | defer wg.Done()
43 | _, _ = Client.ActionComment(context.Background(), &comment.ActionCommentRequest{
44 | ActorId: 1,
45 | VideoId: 1,
46 | ActionType: comment.ActionCommentType_ACTION_COMMENT_TYPE_ADD,
47 | Action: &comment.ActionCommentRequest_CommentText{CommentText: "富强民主文明和谐"},
48 | })
49 | _, _ = Client.ActionComment(context.Background(), &comment.ActionCommentRequest{
50 | ActorId: 2,
51 | VideoId: 1,
52 | ActionType: comment.ActionCommentType_ACTION_COMMENT_TYPE_ADD,
53 | Action: &comment.ActionCommentRequest_CommentText{CommentText: "自由平等公正法治"},
54 | })
55 | }()
56 | }
57 | wg.Wait()
58 | }
59 |
60 | func TestActionComment_Delete(t *testing.T) {
61 | res, err := Client.ActionComment(context.Background(), &comment.ActionCommentRequest{
62 | ActorId: 1,
63 | VideoId: 0,
64 | ActionType: comment.ActionCommentType_ACTION_COMMENT_TYPE_DELETE,
65 | Action: &comment.ActionCommentRequest_CommentId{CommentId: 1},
66 | })
67 | assert.Empty(t, err)
68 | assert.Equal(t, int32(0), res.StatusCode)
69 | }
70 |
71 | func TestListComment(t *testing.T) {
72 | res, err := Client.ListComment(context.Background(), &comment.ListCommentRequest{
73 | ActorId: 1,
74 | VideoId: 0,
75 | })
76 | assert.Empty(t, err)
77 | assert.Equal(t, int32(0), res.StatusCode)
78 | }
79 |
80 | func TestCountComment(t *testing.T) {
81 | res, err := Client.CountComment(context.Background(), &comment.CountCommentRequest{
82 | ActorId: 1,
83 | VideoId: 0,
84 | })
85 | assert.Empty(t, err)
86 | assert.Equal(t, int32(0), res.StatusCode)
87 | }
88 |
89 | func TestMain(m *testing.M) {
90 | setup()
91 | code := m.Run()
92 | os.Exit(code)
93 | }
94 |
--------------------------------------------------------------------------------
/test/rpc/feedrpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/feed"
6 | "context"
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/credentials/insecure"
11 | "testing"
12 | "time"
13 | )
14 |
15 | func TestListVideos(t *testing.T) {
16 |
17 | var Client feed.FeedServiceClient
18 | currentTime := time.Now().Unix()
19 | latestTime := fmt.Sprintf("%d", currentTime)
20 | actorId := uint32(1)
21 | req := feed.ListFeedRequest{
22 | LatestTime: &latestTime,
23 | ActorId: &actorId,
24 | }
25 |
26 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.FeedRpcServerPort),
27 | grpc.WithTransportCredentials(insecure.NewCredentials()),
28 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
29 | assert.Empty(t, err)
30 | Client = feed.NewFeedServiceClient(conn)
31 |
32 | res, err := Client.ListVideos(context.Background(), &req)
33 | assert.Empty(t, err)
34 | assert.Equal(t, int32(0), res.StatusCode)
35 | }
36 |
37 | func TestQueryVideoExisted(t *testing.T) {
38 |
39 | var Client feed.FeedServiceClient
40 | req := feed.VideoExistRequest{
41 | VideoId: 1,
42 | }
43 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.FeedRpcServerPort),
44 | grpc.WithTransportCredentials(insecure.NewCredentials()),
45 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
46 | assert.Empty(t, err)
47 | Client = feed.NewFeedServiceClient(conn)
48 |
49 | res, err := Client.QueryVideoExisted(context.Background(), &req)
50 | assert.Empty(t, err)
51 | assert.Equal(t, int32(0), res.StatusCode)
52 | }
53 |
--------------------------------------------------------------------------------
/test/rpc/like_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/favorite"
6 | "context"
7 | "fmt"
8 | "testing"
9 |
10 | "github.com/stretchr/testify/assert"
11 | "google.golang.org/grpc"
12 | "google.golang.org/grpc/credentials/insecure"
13 | )
14 |
15 | var likeClient favorite.FavoriteServiceClient
16 |
17 | func setups1() {
18 | conn, _ := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.FavoriteRpcServerPort),
19 | grpc.WithTransportCredentials(insecure.NewCredentials()),
20 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
21 | likeClient = favorite.NewFavoriteServiceClient(conn)
22 | }
23 | func TestFavoriteAction(t *testing.T) {
24 | setups1()
25 | res, err := likeClient.FavoriteAction(context.Background(), &favorite.FavoriteRequest{
26 | ActorId: 2,
27 | VideoId: 20,
28 | ActionType: 1,
29 | })
30 | assert.Empty(t, err)
31 | assert.Equal(t, int32(0), res.StatusCode)
32 | }
33 |
34 | func TestFavoriteList(t *testing.T) {
35 | setups1()
36 | res, err := likeClient.FavoriteList(context.Background(), &favorite.FavoriteListRequest{
37 | ActorId: 2,
38 | UserId: 1,
39 | })
40 |
41 | assert.Empty(t, err)
42 | assert.Equal(t, int32(0), res.StatusCode)
43 | assert.Nil(t, res.VideoList)
44 | }
45 |
46 | func TestIsFavorite(t *testing.T) {
47 | setups1()
48 | res, err := likeClient.IsFavorite(context.Background(), &favorite.IsFavoriteRequest{
49 | ActorId: 1,
50 | VideoId: 1,
51 | })
52 | assert.Empty(t, err)
53 | assert.Equal(t, int32(0), res.StatusCode)
54 | assert.Equal(t, true, res.Result)
55 | }
56 |
57 | func TestCountFavorite(t *testing.T) {
58 | setups1()
59 | res, err := likeClient.CountFavorite(context.Background(), &favorite.CountFavoriteRequest{
60 | VideoId: 88,
61 | })
62 | assert.Empty(t, err)
63 | assert.Equal(t, int32(0), res.StatusCode)
64 | assert.Equal(t, uint32(1), res.Count)
65 | }
66 |
67 | func TestCountUserFavorite(t *testing.T) {
68 | setups1()
69 | res, err := likeClient.CountUserFavorite(context.Background(), &favorite.CountUserFavoriteRequest{
70 | UserId: 2,
71 | })
72 | assert.Empty(t, err)
73 | assert.Equal(t, int32(0), res.StatusCode)
74 | assert.Equal(t, uint32(1), res.Count)
75 | }
76 |
77 | func TestCountUserTotalFavorited(t *testing.T) {
78 | setups1()
79 | res, err := likeClient.CountUserTotalFavorited(context.Background(), &favorite.CountUserTotalFavoritedRequest{
80 | ActorId: 100,
81 | UserId: 3,
82 | })
83 | assert.Empty(t, err)
84 | assert.Equal(t, int32(0), res.StatusCode)
85 | assert.Equal(t, uint32(0), res.Count)
86 | }
87 |
--------------------------------------------------------------------------------
/test/rpc/messagerpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/chat"
6 | "context"
7 | "testing"
8 |
9 | "github.com/stretchr/testify/assert"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/credentials/insecure"
12 | )
13 |
14 | var chatClient chat.ChatServiceClient
15 |
16 | func setups() {
17 | conn, _ := grpc.Dial(config.MessageRpcServerPort,
18 | grpc.WithTransportCredentials(insecure.NewCredentials()),
19 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
20 | chatClient = chat.NewChatServiceClient(conn)
21 | }
22 |
23 | func TestActionMessage_Add(t *testing.T) {
24 | setups()
25 | res, err := chatClient.ChatAction(context.Background(), &chat.ActionRequest{
26 | ActorId: 1,
27 | UserId: 2,
28 | ActionType: 1,
29 | Content: "Test message1",
30 | })
31 |
32 | assert.Empty(t, err)
33 | assert.Equal(t, int32(0), res.StatusCode)
34 |
35 | }
36 |
37 | func TestChat(t *testing.T) {
38 | setups()
39 | res, err := chatClient.Chat(context.Background(), &chat.ChatRequest{
40 | ActorId: 1,
41 | UserId: 2,
42 | PreMsgTime: 0,
43 | })
44 |
45 | assert.Empty(t, err)
46 | assert.Equal(t, int32(0), res.StatusCode)
47 | }
48 |
--------------------------------------------------------------------------------
/test/rpc/publishrpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/publish"
6 | "context"
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/credentials/insecure"
11 | "io"
12 | "os"
13 | "sync"
14 | "testing"
15 | )
16 |
17 | var publishClient publish.PublishServiceClient
18 |
19 | func TestListVideo(t *testing.T) {
20 | req := publish.ListVideoRequest{
21 | UserId: 123,
22 | ActorId: 123,
23 | }
24 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.PublishRpcServerPort),
25 | grpc.WithTransportCredentials(insecure.NewCredentials()),
26 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
27 | assert.Empty(t, err)
28 | publishClient = publish.NewPublishServiceClient(conn)
29 | //调用服务端方法
30 | res, err := publishClient.ListVideo(context.Background(), &req)
31 | assert.Empty(t, err)
32 | assert.Equal(t, int32(0), res.StatusCode)
33 | }
34 |
35 | func TestCountVideo(t *testing.T) {
36 | req := publish.CountVideoRequest{
37 | UserId: 1,
38 | }
39 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.PublishRpcServerPort),
40 | grpc.WithTransportCredentials(insecure.NewCredentials()),
41 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
42 | assert.Empty(t, err)
43 | publishClient = publish.NewPublishServiceClient(conn)
44 | res, err := publishClient.CountVideo(context.Background(), &req)
45 | assert.Empty(t, err)
46 | assert.Equal(t, int32(0), res.StatusCode)
47 | }
48 |
49 | func TestPublishVideo(t *testing.T) {
50 | reader, err := os.Open("/home/yangfeng/Repos/youthcamp/videos/upload_video_2_1080p.mp4")
51 | assert.Empty(t, err)
52 | bytes, err := io.ReadAll(reader)
53 | assert.Empty(t, err)
54 | req := publish.CreateVideoRequest{
55 | ActorId: 2,
56 | Data: bytes,
57 | Title: "原神,启动!",
58 | }
59 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.PublishRpcServerPort),
60 | grpc.WithTransportCredentials(insecure.NewCredentials()),
61 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
62 | assert.Empty(t, err)
63 | publishClient = publish.NewPublishServiceClient(conn)
64 | res, err := publishClient.CreateVideo(context.Background(), &req)
65 | assert.Empty(t, err)
66 | assert.Equal(t, int32(0), res.StatusCode)
67 | }
68 |
69 | func TestPublishVideo_Limiter(t *testing.T) {
70 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.PublishRpcServerPort),
71 | grpc.WithTransportCredentials(insecure.NewCredentials()),
72 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
73 | assert.Empty(t, err)
74 | publishClient = publish.NewPublishServiceClient(conn)
75 |
76 | reader, err := os.Open("/home/yangfeng/Repos/youthcamp/videos/upload_video_4.mp4")
77 | assert.Empty(t, err)
78 | bytes, err := io.ReadAll(reader)
79 | assert.Empty(t, err)
80 | req := publish.CreateVideoRequest{
81 | ActorId: 1,
82 | Data: bytes,
83 | Title: "原神,启动!",
84 | }
85 |
86 | wg := &sync.WaitGroup{}
87 | for i := 0; i < 10; i++ {
88 | wg.Add(1)
89 | go func() {
90 | defer wg.Done()
91 | _, _ = publishClient.CreateVideo(context.Background(), &req)
92 | }()
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/test/rpc/userrpc_test.go:
--------------------------------------------------------------------------------
1 | package rpc
2 |
3 | import (
4 | "GuGoTik/src/constant/config"
5 | "GuGoTik/src/rpc/user"
6 | "context"
7 | "fmt"
8 | "github.com/stretchr/testify/assert"
9 | "google.golang.org/grpc"
10 | "google.golang.org/grpc/credentials/insecure"
11 | "testing"
12 | )
13 |
14 | func TestGetUserInfo(t *testing.T) {
15 | var Client user.UserServiceClient
16 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.UserRpcServerPort),
17 | grpc.WithTransportCredentials(insecure.NewCredentials()),
18 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
19 | assert.Empty(t, err)
20 | Client = user.NewUserServiceClient(conn)
21 | res, err := Client.GetUserInfo(context.Background(), &user.UserRequest{
22 | UserId: 2,
23 | ActorId: 1,
24 | })
25 | assert.Empty(t, err)
26 | assert.Equal(t, int32(0), res.StatusCode)
27 | }
28 |
29 | func TestGetUserExistedInfo(t *testing.T) {
30 | var Client user.UserServiceClient
31 | conn, err := grpc.Dial(fmt.Sprintf("127.0.0.1%s", config.UserRpcServerPort),
32 | grpc.WithTransportCredentials(insecure.NewCredentials()),
33 | grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`))
34 | assert.Empty(t, err)
35 | Client = user.NewUserServiceClient(conn)
36 | res, err := Client.GetUserExistInformation(context.Background(), &user.UserExistRequest{
37 | UserId: 1,
38 | })
39 | assert.Empty(t, err)
40 | assert.Equal(t, int32(0), res.StatusCode)
41 | }
42 |
--------------------------------------------------------------------------------
/test/web/auth_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "encoding/json"
6 | "io"
7 | "net/http"
8 | "testing"
9 |
10 | "github.com/google/uuid"
11 | "github.com/stretchr/testify/assert"
12 | )
13 |
14 | func TestRegister(t *testing.T) {
15 | url := "http://127.0.0.1:37000/douyin/user/register?username=" + uuid.New().String() + "&password=epicmo"
16 | method := "POST"
17 | client := &http.Client{}
18 | req, err := http.NewRequest(method, url, nil)
19 |
20 | assert.Empty(t, err)
21 |
22 | res, err := client.Do(req)
23 | assert.Empty(t, err)
24 | defer func(Body io.ReadCloser) {
25 | err := Body.Close()
26 | assert.Empty(t, err)
27 | }(res.Body)
28 |
29 | body, err := io.ReadAll(res.Body)
30 | assert.Empty(t, err)
31 | user := &models.LoginRes{}
32 | err = json.Unmarshal(body, &user)
33 | assert.Empty(t, err)
34 | assert.Equal(t, 0, user.StatusCode)
35 | }
36 |
37 | // This Test can only run once.
38 | func TestDisplayRegister(t *testing.T) {
39 | url := "http://127.0.0.1:37000/douyin/user/register?username=epicmo4&password=epicmo"
40 | method := "POST"
41 | client := &http.Client{}
42 | req, err := http.NewRequest(method, url, nil)
43 |
44 | assert.Empty(t, err)
45 |
46 | res, err := client.Do(req)
47 | assert.Empty(t, err)
48 | defer func(Body io.ReadCloser) {
49 | err := Body.Close()
50 | assert.Empty(t, err)
51 | }(res.Body)
52 |
53 | body, err := io.ReadAll(res.Body)
54 | assert.Empty(t, err)
55 | user := &models.LoginRes{}
56 | err = json.Unmarshal(body, &user)
57 | assert.Empty(t, err)
58 | assert.Equal(t, 0, user.StatusCode)
59 | }
60 |
61 | // This test must run after `TestDisplayRegister`
62 | func TestLogin(t *testing.T) {
63 |
64 | url := "http://127.0.0.1:37000/douyin/user/login?username=epicmo&password=epicmo"
65 | method := "POST"
66 | client := &http.Client{}
67 | req, err := http.NewRequest(method, url, nil)
68 |
69 | assert.Empty(t, err)
70 |
71 | res, err := client.Do(req)
72 | assert.Empty(t, err)
73 | defer func(Body io.ReadCloser) {
74 | err := Body.Close()
75 | assert.Empty(t, err)
76 | }(res.Body)
77 |
78 | body, err := io.ReadAll(res.Body)
79 | assert.Empty(t, err)
80 | user := &models.LoginRes{}
81 | err = json.Unmarshal(body, &user)
82 | assert.Empty(t, err)
83 | assert.Equal(t, 0, user.StatusCode)
84 | }
85 |
--------------------------------------------------------------------------------
/test/web/comment_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "encoding/json"
6 | "github.com/stretchr/testify/assert"
7 | "io"
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | var client = &http.Client{}
13 | var commentBaseUrl = "http://127.0.0.1:37000/douyin/comment"
14 |
15 | func TestActionComment_Add(t *testing.T) {
16 | url := commentBaseUrl + "/action"
17 | method := "POST"
18 | req, err := http.NewRequest(method, url, nil)
19 | q := req.URL.Query()
20 | q.Add("token", token)
21 | q.Add("actor_id", "1")
22 | q.Add("video_id", "0")
23 | q.Add("action_type", "1")
24 | q.Add("comment_text", "test comment")
25 | req.URL.RawQuery = q.Encode()
26 |
27 | assert.Empty(t, err)
28 |
29 | res, err := client.Do(req)
30 | assert.Empty(t, err)
31 | defer func(Body io.ReadCloser) {
32 | err := Body.Close()
33 | assert.Empty(t, err)
34 | }(res.Body)
35 |
36 | body, err := io.ReadAll(res.Body)
37 | assert.Empty(t, err)
38 | comment := &models.ActionCommentRes{}
39 | err = json.Unmarshal(body, &comment)
40 | assert.Empty(t, err)
41 | assert.Equal(t, 0, comment.StatusCode)
42 | }
43 |
44 | func TestActionComment_Delete(t *testing.T) {
45 | url := commentBaseUrl + "/action"
46 | method := "POST"
47 | req, err := http.NewRequest(method, url, nil)
48 | q := req.URL.Query()
49 | q.Add("token", token)
50 | q.Add("actor_id", "1")
51 | q.Add("video_id", "0")
52 | q.Add("action_type", "2")
53 | q.Add("comment_id", "2")
54 | req.URL.RawQuery = q.Encode()
55 |
56 | assert.Empty(t, err)
57 |
58 | res, err := client.Do(req)
59 | assert.Empty(t, err)
60 | defer func(Body io.ReadCloser) {
61 | err := Body.Close()
62 | assert.Empty(t, err)
63 | }(res.Body)
64 |
65 | body, err := io.ReadAll(res.Body)
66 | assert.Empty(t, err)
67 | actionCommentRes := &models.ActionCommentRes{}
68 | err = json.Unmarshal(body, &actionCommentRes)
69 | assert.Empty(t, err)
70 | assert.Equal(t, 0, actionCommentRes.StatusCode)
71 | }
72 |
73 | func TestListComment(t *testing.T) {
74 | url := commentBaseUrl + "/list"
75 | method := "GET"
76 | req, err := http.NewRequest(method, url, nil)
77 | q := req.URL.Query()
78 | q.Add("token", token)
79 | q.Add("actor_id", "1")
80 | q.Add("video_id", "0")
81 | req.URL.RawQuery = q.Encode()
82 |
83 | assert.Empty(t, err)
84 |
85 | res, err := client.Do(req)
86 | assert.Empty(t, err)
87 | defer func(Body io.ReadCloser) {
88 | err := Body.Close()
89 | assert.Empty(t, err)
90 | }(res.Body)
91 |
92 | body, err := io.ReadAll(res.Body)
93 | assert.Empty(t, err)
94 | listCommentRes := &models.ListCommentRes{}
95 | err = json.Unmarshal(body, &listCommentRes)
96 | assert.Empty(t, err)
97 | assert.Equal(t, 0, listCommentRes.StatusCode)
98 | }
99 |
100 | func TestCountComment(t *testing.T) {
101 | url := commentBaseUrl + "/count"
102 | method := "GET"
103 | req, err := http.NewRequest(method, url, nil)
104 | q := req.URL.Query()
105 | q.Add("token", token)
106 | q.Add("actor_id", "1")
107 | q.Add("video_id", "0")
108 | req.URL.RawQuery = q.Encode()
109 |
110 | assert.Empty(t, err)
111 |
112 | res, err := client.Do(req)
113 | assert.Empty(t, err)
114 | defer func(Body io.ReadCloser) {
115 | err := Body.Close()
116 | assert.Empty(t, err)
117 | }(res.Body)
118 |
119 | body, err := io.ReadAll(res.Body)
120 | assert.Empty(t, err)
121 | countCommentRes := &models.CountCommentRes{}
122 | err = json.Unmarshal(body, &countCommentRes)
123 | assert.Empty(t, err)
124 | assert.Equal(t, 0, countCommentRes.StatusCode)
125 | }
126 |
--------------------------------------------------------------------------------
/test/web/favorite_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "encoding/json"
6 | "github.com/stretchr/testify/assert"
7 | "io"
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | var favoriteBaseUrl = "http://127.0.0.1:37000/douyin/favorite"
13 |
14 | func TestActionFavorite_Do(t *testing.T) {
15 | url := favoriteBaseUrl + "/action"
16 | method := "POST"
17 | req, err := http.NewRequest(method, url, nil)
18 | q := req.URL.Query()
19 | q.Add("token", "") // replace token, video_id
20 | q.Add("video_id", "1948195853")
21 | q.Add("action_type", "1")
22 | req.URL.RawQuery = q.Encode()
23 |
24 | assert.Empty(t, err)
25 |
26 | res, err := client.Do(req)
27 | assert.Empty(t, err)
28 | defer func(Body io.ReadCloser) {
29 | err := Body.Close()
30 | assert.Empty(t, err)
31 | }(res.Body)
32 |
33 | body, err := io.ReadAll(res.Body)
34 | assert.Empty(t, err)
35 | actionFavoriteRes := &models.ActionFavoriteRes{}
36 | err = json.Unmarshal(body, &actionFavoriteRes)
37 | assert.Empty(t, err)
38 | assert.Equal(t, 0, actionFavoriteRes.StatusCode)
39 | }
40 |
41 | func TestActionFavorite_Cancel(t *testing.T) {
42 | url := favoriteBaseUrl + "/action"
43 | method := "POST"
44 | req, err := http.NewRequest(method, url, nil)
45 | q := req.URL.Query()
46 | q.Add("token", "") // replace token, video_id
47 | q.Add("video_id", "1948195853")
48 | q.Add("action_type", "2")
49 | req.URL.RawQuery = q.Encode()
50 |
51 | assert.Empty(t, err)
52 |
53 | res, err := client.Do(req)
54 | assert.Empty(t, err)
55 | defer func(Body io.ReadCloser) {
56 | err := Body.Close()
57 | assert.Empty(t, err)
58 | }(res.Body)
59 |
60 | body, err := io.ReadAll(res.Body)
61 | assert.Empty(t, err)
62 | actionFavoriteRes := &models.ActionFavoriteRes{}
63 | err = json.Unmarshal(body, &actionFavoriteRes)
64 | assert.Empty(t, err)
65 | assert.Equal(t, 0, actionFavoriteRes.StatusCode)
66 | }
67 |
68 | func TestListFavorite(t *testing.T) {
69 | url := favoriteBaseUrl + "/list"
70 | method := "POST"
71 | req, err := http.NewRequest(method, url, nil)
72 | q := req.URL.Query()
73 | q.Add("token", "") // replace token, user_id
74 | q.Add("user_id", "1")
75 | req.URL.RawQuery = q.Encode()
76 |
77 | assert.Empty(t, err)
78 |
79 | res, err := client.Do(req)
80 | assert.Empty(t, err)
81 | defer func(Body io.ReadCloser) {
82 | err := Body.Close()
83 | assert.Empty(t, err)
84 | }(res.Body)
85 |
86 | body, err := io.ReadAll(res.Body)
87 | assert.Empty(t, err)
88 | listFavoriteRes := &models.ListFavoriteRes{}
89 | err = json.Unmarshal(body, &listFavoriteRes)
90 | assert.Empty(t, err)
91 | assert.Equal(t, 0, listFavoriteRes.StatusCode)
92 | }
93 |
--------------------------------------------------------------------------------
/test/web/feed_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "encoding/json"
6 | "github.com/stretchr/testify/assert"
7 | "io"
8 | "net/http"
9 | "testing"
10 | )
11 |
12 | func TestListVideos(t *testing.T) {
13 | //url := "http://127.0.0.1:37000/douyin/feed/?token=90aee89f-43c0-4e90-a440-cf4e47c9b790"
14 | url := "http://127.0.0.1:37000/douyin/feed/?latest_time=2006-01-02T15:04:05.999Z&token=90aee89f-43c0-4e90-a440-cf4e47c9b790"
15 |
16 | method := "GET"
17 | client := &http.Client{}
18 | req, err := http.NewRequest(method, url, nil)
19 | assert.Empty(t, err)
20 |
21 | res, err := client.Do(req)
22 | assert.Empty(t, err)
23 | defer func(Body io.ReadCloser) {
24 | err := Body.Close()
25 | assert.Empty(t, err)
26 | }(res.Body)
27 |
28 | body, err := io.ReadAll(res.Body)
29 | assert.Empty(t, err)
30 | feed := &models.ListVideosRes{}
31 | err = json.Unmarshal(body, &feed)
32 | assert.Empty(t, err)
33 | assert.Equal(t, 0, feed.StatusCode)
34 | }
35 |
--------------------------------------------------------------------------------
/test/web/message_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "encoding/json"
6 | "fmt"
7 | "io"
8 | "net/http"
9 | "testing"
10 |
11 | "github.com/stretchr/testify/assert"
12 | )
13 |
14 | func TestActionMessage_Add(t *testing.T) {
15 |
16 | var client = &http.Client{}
17 | var baseUrl = "http://127.0.0.1:37000/douyin/message"
18 | url := baseUrl + "/action"
19 | method := "POST"
20 | req, err := http.NewRequest(method, url, nil)
21 | q := req.URL.Query()
22 | q.Add("token", token)
23 | q.Add("to_user_id", "2")
24 | q.Add("action_type", "1")
25 | q.Add("content", "test comment in gateway")
26 | req.URL.RawQuery = q.Encode()
27 |
28 | assert.Empty(t, err)
29 |
30 | res, err := client.Do(req)
31 | assert.Empty(t, err)
32 | defer func(Body io.ReadCloser) {
33 | err := Body.Close()
34 | assert.Empty(t, err)
35 | }(res.Body)
36 |
37 | body, err := io.ReadAll(res.Body)
38 | assert.Empty(t, err)
39 | message := &models.ListMessageRes{}
40 | err = json.Unmarshal(body, &message)
41 | assert.Empty(t, err)
42 | assert.Equal(t, 0, message.StatusCode)
43 | }
44 |
45 | func TestChat(t *testing.T) {
46 | var client = &http.Client{}
47 | var baseUrl = "http://127.0.0.1:37000/douyin/message"
48 | url := baseUrl + "/chat"
49 | method := "GET"
50 | req, err := http.NewRequest(method, url, nil)
51 |
52 | q := req.URL.Query()
53 | q.Add("token", "token")
54 | q.Add("to_user_id", "2")
55 | q.Add("perMsgTime", "0")
56 | req.URL.RawQuery = q.Encode()
57 | assert.Empty(t, err)
58 |
59 | res, err := client.Do(req)
60 | assert.Empty(t, err)
61 | defer func(Body io.ReadCloser) {
62 | err := Body.Close()
63 | assert.Empty(t, err)
64 | }(res.Body)
65 |
66 | body, err := io.ReadAll(res.Body)
67 | assert.Empty(t, err)
68 | listMessage := &models.ListMessageRes{}
69 | fmt.Println(listMessage)
70 | err = json.Unmarshal(body, &listMessage)
71 | assert.Empty(t, err)
72 | assert.Equal(t, 0, listMessage.StatusCode)
73 | }
74 |
--------------------------------------------------------------------------------
/test/web/publish_test.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | import (
4 | "GuGoTik/src/web/models"
5 | "bytes"
6 | "encoding/json"
7 | "github.com/stretchr/testify/assert"
8 | "io"
9 | "mime/multipart"
10 | "net/http"
11 | "os"
12 | "testing"
13 | )
14 |
15 | func TestListVideo(t *testing.T) {
16 | url := "http://127.0.0.1:37000/douyin/publish/list"
17 | method := "GET"
18 | req, err := http.NewRequest(method, url, nil)
19 | q := req.URL.Query()
20 | q.Add("token", token)
21 | q.Add("user_id", "1")
22 | req.URL.RawQuery = q.Encode()
23 |
24 | assert.Empty(t, err)
25 |
26 | res, err := client.Do(req)
27 | assert.Empty(t, err)
28 | defer func(Body io.ReadCloser) {
29 | err := Body.Close()
30 | assert.Empty(t, err)
31 | }(res.Body)
32 |
33 | body, err := io.ReadAll(res.Body)
34 | assert.Empty(t, err)
35 | ListPublishRes := &models.ListPublishRes{}
36 | err = json.Unmarshal(body, &ListPublishRes)
37 | assert.Empty(t, err)
38 | assert.Equal(t, 0, ListPublishRes.StatusCode)
39 | }
40 |
41 | func TestPublishVideo(t *testing.T) {
42 | url := "http://127.0.0.1:37000/douyin/publish/action"
43 | method := "POST"
44 | filePath := "E:\\Administrator\\Videos\\1.mp4"
45 |
46 | payload := &bytes.Buffer{}
47 | writer := multipart.NewWriter(payload)
48 |
49 | file, err := os.Open(filePath)
50 | assert.Empty(t, err)
51 | defer func(file *os.File) {
52 | err := file.Close()
53 | assert.Empty(t, err)
54 | }(file)
55 |
56 | fileWriter, err := writer.CreateFormFile("data", file.Name())
57 | assert.Empty(t, err)
58 |
59 | _, err = io.Copy(fileWriter, file)
60 | assert.Empty(t, err)
61 |
62 | _ = writer.WriteField("token", token)
63 | _ = writer.WriteField("title", "10个报错,但是我代码只有9行啊???")
64 |
65 | err = writer.Close()
66 | assert.Empty(t, err)
67 |
68 | req, err := http.NewRequest(method, url, payload)
69 | assert.Empty(t, err)
70 |
71 | req.Header.Set("Content-Type", writer.FormDataContentType())
72 |
73 | res, err := client.Do(req)
74 | assert.Empty(t, err)
75 | defer func(Body io.ReadCloser) {
76 | err := Body.Close()
77 | assert.Empty(t, err)
78 | }(res.Body)
79 |
80 | body, err := io.ReadAll(res.Body)
81 | assert.Empty(t, err)
82 | actionPublishRes := &models.ActionPublishRes{}
83 | err = json.Unmarshal(body, &actionPublishRes)
84 | assert.Empty(t, err)
85 | assert.Equal(t, 0, actionPublishRes.StatusCode)
86 | }
87 |
--------------------------------------------------------------------------------
/test/web/value.go:
--------------------------------------------------------------------------------
1 | package web
2 |
3 | const token = "e75fae76-6a4e-4fa8-9b60-230e5d4f6b29"
4 |
--------------------------------------------------------------------------------