├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── tests.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── README_zh.md ├── deployment ├── config │ └── consul-config.json └── freecar-k8s │ ├── Makefile │ ├── auth.yaml │ ├── consul │ ├── Makefile │ ├── consul.yaml │ ├── pv.yaml │ └── pvc.yaml │ ├── dashboard.yaml │ ├── minio │ ├── Makefile │ ├── minio.yaml │ ├── pv.yaml │ └── pvc.yaml │ ├── mongodb │ ├── Makefile │ ├── mongodb.yaml │ ├── pv.yaml │ └── pvc.yaml │ ├── mysql │ ├── Makefile │ ├── mysql.yaml │ ├── pv.yaml │ └── pvc.yaml │ ├── rabbitmq │ ├── Makefile │ ├── pv.yaml │ ├── pvc.yaml │ └── rabbitmq.yaml │ └── redis │ ├── Makefile │ ├── pv.yaml │ ├── pvc.yaml │ └── redis.yaml ├── docker-compose.yaml ├── docs └── CONSUL_CONFIG.md ├── go.mod ├── go.sum ├── img ├── FreeCar.png ├── back.png ├── call_relation.png ├── consul_kv.png ├── consul_service.png ├── data-analize.png ├── display.png ├── jaeger.png ├── jaeger2.png ├── minio.png ├── pod.png ├── prometheus.png ├── pvc.png ├── service.png ├── service_relations.png └── tech_arch.png ├── licenses ├── LICENSE-amqp ├── LICENSE-consul ├── LICENSE-cos-go-skd-v5 ├── LICENSE-go-connections ├── LICENSE-gorm ├── LICENSE-hertz ├── LICENSE-jwt ├── LICENSE-kitex ├── LICENSE-lumberjack ├── LICENSE-moby ├── LICENSE-mongo-go-driver ├── LICENSE-nacos-sdk-go ├── LICENSE-protobuf-go ├── LICENSE-snowflake ├── LICENSE-sonic ├── LICENSE-thrift ├── LICENSE-viper └── LICENSE-weapp ├── otel-collector-config.yaml └── server ├── cmd ├── api │ ├── .hz │ ├── Makefile │ ├── biz │ │ ├── handler │ │ │ ├── car │ │ │ │ └── car_service.go │ │ │ ├── gpt │ │ │ │ └── gpt_service.go │ │ │ ├── ping.go │ │ │ ├── profile │ │ │ │ └── profile_service.go │ │ │ ├── trip │ │ │ │ └── trip_service.go │ │ │ └── user │ │ │ │ └── user_service.go │ │ ├── model │ │ │ ├── base │ │ │ │ ├── car.go │ │ │ │ ├── common.go │ │ │ │ ├── profile.go │ │ │ │ ├── trip.go │ │ │ │ └── user.go │ │ │ ├── car │ │ │ │ └── car.go │ │ │ ├── gpt │ │ │ │ └── gpt.go │ │ │ ├── profile │ │ │ │ └── profile.go │ │ │ ├── trip │ │ │ │ └── trip.go │ │ │ └── user │ │ │ │ └── user.go │ │ └── router │ │ │ ├── car │ │ │ ├── car.go │ │ │ └── middleware.go │ │ │ ├── common │ │ │ └── common.go │ │ │ ├── gpt │ │ │ ├── gpt.go │ │ │ └── middleware.go │ │ │ ├── profile │ │ │ ├── middleware.go │ │ │ └── profile.go │ │ │ ├── register.go │ │ │ ├── trip │ │ │ ├── middleware.go │ │ │ └── trip.go │ │ │ └── user │ │ │ ├── middleware.go │ │ │ └── user.go │ ├── config.yaml │ ├── config │ │ ├── config.go │ │ └── global.go │ ├── initialize │ │ ├── config.go │ │ ├── cors.go │ │ ├── logger.go │ │ ├── registry.go │ │ ├── rpc │ │ │ ├── car_service.go │ │ │ ├── init.go │ │ │ ├── profile_service.go │ │ │ ├── trip_service.go │ │ │ └── user_service.go │ │ ├── sentinel.go │ │ └── tls.go │ ├── main.go │ ├── pkg │ │ └── convert.go │ ├── router.go │ └── router_gen.go ├── blob │ ├── Makefile │ ├── config.yaml │ ├── config │ │ ├── config.go │ │ └── global.go │ ├── handler.go │ ├── initialize │ │ ├── config.go │ │ ├── db.go │ │ ├── flag.go │ │ ├── logger.go │ │ ├── minio.go │ │ └── registry.go │ ├── kitex.yaml │ ├── main.go │ └── pkg │ │ ├── minio │ │ └── minio.go │ │ └── mysql │ │ └── mysql.go ├── car │ ├── Makefile │ ├── config.yaml │ ├── config │ │ ├── config.go │ │ └── global.go │ ├── handler.go │ ├── initialize │ │ ├── car_service.go │ │ ├── config.go │ │ ├── db.go │ │ ├── flag.go │ │ ├── logger.go │ │ ├── mq.go │ │ ├── registry.go │ │ └── trip_service.go │ ├── kitex.yaml │ ├── main.go │ └── pkg │ │ ├── mongo │ │ ├── mongo.go │ │ └── mongo_test.go │ │ ├── mq │ │ ├── amqpclt │ │ │ └── amqp.go │ │ └── mq.go │ │ ├── redis │ │ ├── redis.go │ │ └── redis_test.go │ │ ├── sim │ │ └── sim.go │ │ ├── trip │ │ └── updater.go │ │ └── ws │ │ └── ws.go ├── profile │ ├── Makefile │ ├── config.yaml │ ├── config │ │ ├── config.go │ │ └── global.go │ ├── handler.go │ ├── initialize │ │ ├── blob_service.go │ │ ├── config.go │ │ ├── db.go │ │ ├── flag.go │ │ ├── logger.go │ │ └── registry.go │ ├── kitex.yaml │ ├── main.go │ └── pkg │ │ ├── mongo │ │ ├── mongo.go │ │ └── mongo_test.go │ │ ├── ocr │ │ └── client.go │ │ └── redis │ │ ├── redis.go │ │ └── redis_test.go ├── trade │ ├── .gitattributes │ ├── .gitignore │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── mvnw │ ├── pom.xml │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── lanlance │ │ │ │ └── freecartrade │ │ │ │ ├── FreecarTradeApplication.java │ │ │ │ ├── annotation │ │ │ │ ├── MonitorLog.java │ │ │ │ └── aspect │ │ │ │ │ └── MonitorLogAspect.java │ │ │ │ ├── config │ │ │ │ ├── CryptoConfig.java │ │ │ │ ├── PasetoConfig.java │ │ │ │ ├── PaymentStatusEnum.java │ │ │ │ ├── RabbitConfig.java │ │ │ │ └── WebMvcConfig.java │ │ │ │ ├── controller │ │ │ │ └── TradeController.java │ │ │ │ ├── cron │ │ │ │ └── PaymentReconciliationTask.java │ │ │ │ ├── interceptor │ │ │ │ └── PasetoAuthInterceptor.java │ │ │ │ ├── model │ │ │ │ ├── PayInfo.java │ │ │ │ ├── RedisDeductResult.java │ │ │ │ ├── req │ │ │ │ │ └── RechargeRequest.java │ │ │ │ └── resp │ │ │ │ │ └── Result.java │ │ │ │ ├── mq │ │ │ │ └── TradeMessageListener.java │ │ │ │ ├── repository │ │ │ │ ├── mapper │ │ │ │ │ └── UserMapper.java │ │ │ │ ├── mongo │ │ │ │ │ └── TripRepository.java │ │ │ │ └── redis │ │ │ │ │ └── RedisRepository.java │ │ │ │ ├── service │ │ │ │ ├── RechargeServiceIface.java │ │ │ │ ├── TradeServiceIface.java │ │ │ │ └── impl │ │ │ │ │ ├── RechargeServiceImpl.java │ │ │ │ │ └── TradeServiceImpl.java │ │ │ │ └── util │ │ │ │ ├── CalcUtil.java │ │ │ │ └── ResultUtil.java │ │ └── resources │ │ │ ├── application.properties │ │ │ ├── bootstrap.yml │ │ │ ├── logback.xml │ │ │ └── lua │ │ │ └── deduct_balance.lua │ │ └── test │ │ └── java │ │ └── org │ │ └── lanlance │ │ └── freecartrade │ │ └── FreecarTradeApplicationTests.java ├── trip │ ├── Makefile │ ├── config.yaml │ ├── config │ │ ├── config.go │ │ └── global.go │ ├── handler.go │ ├── initialize │ │ ├── car_service.go │ │ ├── config.go │ │ ├── db.go │ │ ├── flag.go │ │ ├── logger.go │ │ ├── mq.go │ │ ├── profile_service.go │ │ └── registry.go │ ├── kitex.yaml │ ├── main.go │ └── pkg │ │ ├── car │ │ └── car.go │ │ ├── mongo │ │ ├── mongo.go │ │ └── mongo_test.go │ │ ├── mq │ │ ├── amqpclt │ │ │ └── amqp.go │ │ ├── model │ │ │ └── PayInfo.go │ │ └── mq.go │ │ ├── poi │ │ └── poi.go │ │ └── profile │ │ └── profile.go └── user │ ├── Makefile │ ├── config.yaml │ ├── config │ ├── config.go │ └── global.go │ ├── handler.go │ ├── initialize │ ├── blob_service.go │ ├── config.go │ ├── db.go │ ├── flag.go │ ├── logger.go │ └── registry.go │ ├── kitex.yaml │ ├── main.go │ └── pkg │ ├── md5 │ └── md5.go │ ├── mysql │ ├── admin.go │ ├── admin_test.go │ ├── user.go │ └── user_test.go │ ├── paseto │ └── paseto.go │ └── wechat │ └── wechat.go ├── idl ├── base │ ├── car.thrift │ ├── common.thrift │ ├── errno.thrift │ ├── profile.thrift │ ├── trip.thrift │ └── user.thrift ├── http │ ├── car.thrift │ ├── gpt.thrift │ ├── profile.thrift │ ├── trip.thrift │ └── user.thrift └── rpc │ ├── blob.thrift │ ├── car.thrift │ ├── profile.thrift │ ├── trip.thrift │ └── user.thrift └── shared ├── Makefile ├── consts └── consts.go ├── errno └── errno.go ├── id └── id.go ├── kitex_gen ├── base │ ├── car.go │ ├── common.go │ ├── k-car.go │ ├── k-common.go │ ├── k-consts.go │ ├── k-profile.go │ ├── k-trip.go │ ├── k-user.go │ ├── profile.go │ ├── trip.go │ └── user.go ├── blob │ ├── blob.go │ ├── blobservice │ │ ├── blobservice.go │ │ ├── client.go │ │ ├── invoker.go │ │ └── server.go │ ├── k-blob.go │ └── k-consts.go ├── car │ ├── car.go │ ├── carservice │ │ ├── carservice.go │ │ ├── client.go │ │ ├── invoker.go │ │ └── server.go │ ├── k-car.go │ └── k-consts.go ├── errno │ ├── errno.go │ ├── k-consts.go │ └── k-errno.go ├── profile │ ├── k-consts.go │ ├── k-profile.go │ ├── profile.go │ └── profileservice │ │ ├── client.go │ │ ├── invoker.go │ │ ├── profileservice.go │ │ └── server.go ├── trip │ ├── k-consts.go │ ├── k-trip.go │ ├── trip.go │ └── tripservice │ │ ├── client.go │ │ ├── invoker.go │ │ ├── server.go │ │ └── tripservice.go └── user │ ├── k-consts.go │ ├── k-user.go │ ├── user.go │ └── userservice │ ├── client.go │ ├── invoker.go │ ├── server.go │ └── userservice.go ├── middleware └── recovery.go ├── mongo ├── mongo.go ├── objid │ └── objid.go └── setup.js ├── test ├── mongo.go ├── mysql.go └── redis.go └── tools ├── ip.go ├── port.go └── resp.go /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: L2ncE 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | **To Reproduce** 15 | 16 | Steps to reproduce the behavior: 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Environment:** 31 | 32 | The output of `go env`. 33 | 34 | **Additional context** 35 | 36 | Add any other context about the problem here. 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | 12 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | 20 | A clear and concise description of any alternative solutions or features you've considered. 21 | 22 | **Additional context** 23 | 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What type of PR is this? 2 | 17 | 18 | #### Check the PR title. 19 | 24 | - [ ] This PR title match the format: \(optional scope): \ 25 | - [ ] The description of this PR title is user-oriented and clear enough for others to understand. 26 | 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | labels: 6 | - "dependencies" 7 | schedule: 8 | interval: "monthly" 9 | ignore: 10 | - dependency-name: "go.opentelemetry.io/otel/trace" 11 | - dependency-name: "github.com/apache/thrift" 12 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | CI: 7 | runs-on: [ ubuntu-latest ] 8 | steps: 9 | - uses: actions/checkout@v3 10 | 11 | - name: Configure Docker daemon 12 | run: | 13 | sudo mkdir -p /etc/docker 14 | echo '{ "experimental": true, "features": { "buildkit": true } }' | sudo tee /etc/docker/daemon.json 15 | sudo systemctl restart docker || sudo service docker restart 16 | 17 | - uses: docker-practice/actions-setup-docker@master 18 | timeout-minutes: 12 19 | - run: | 20 | docker pull mongo:latest 21 | docker pull mysql:latest 22 | docker pull redis:latest 23 | 24 | - name: Set up Go 25 | uses: actions/setup-go@v3 26 | with: 27 | go-version: 1.18 28 | 29 | - uses: actions/cache@v3 30 | with: 31 | path: ~/go/pkg/mod 32 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 33 | restore-keys: | 34 | ${{ runner.os }}-go- 35 | 36 | - name: Unit Test 37 | run: go test ./... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | .idea 18 | .DS_Store 19 | .vscode 20 | __debug_bin 21 | 22 | 23 | node_modules 24 | miniprogram_npm 25 | private.key 26 | 27 | *.log 28 | **@** 29 | data 30 | /server/cmd/api/cert/ 31 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # start the environment of FreeCar 2 | .PHONY: start 3 | start: 4 | docker-compose up -d 5 | 6 | # stop the environment of FreeCar 7 | .PHONY: stop 8 | stop: 9 | docker-compose down 10 | 11 | # run the user 12 | .PHONY: user 13 | user: 14 | go run ./server/cmd/user 15 | 16 | # run the blob 17 | .PHONY: blob 18 | blob: 19 | go run ./server/cmd/blob 20 | 21 | # run the car 22 | .PHONY: car 23 | car: 24 | go run ./server/cmd/car 25 | 26 | # run the profile 27 | .PHONY: profile 28 | profile: 29 | go run ./server/cmd/profile 30 | 31 | # run the trip 32 | .PHONY: trip 33 | trip: 34 | go run ./server/cmd/trip 35 | 36 | # run the api 37 | .PHONY: api 38 | api: 39 | go run ./server/cmd/api -------------------------------------------------------------------------------- /deployment/config/consul-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server_rejoin_age_max": "7200h", 3 | "performance": { 4 | "leave_drain_time": "5m" 5 | } 6 | } -------------------------------------------------------------------------------- /deployment/freecar-k8s/Makefile: -------------------------------------------------------------------------------- 1 | generate: 2 | kubectl apply -f $(basic).yaml 3 | 4 | ingress: 5 | make basic=ingress generate 6 | 7 | dashboard: 8 | make basic=dashboard generate 9 | make basic=auth generate 10 | 11 | all: 12 | make dashboard 13 | make -C consul make 14 | make -C minio make 15 | make -C mongodb make 16 | make -C mysql make 17 | make -C rabbitmq make 18 | make -C redis make 19 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/auth.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: service-controller-cluster-admin 5 | subjects: 6 | - kind: ServiceAccount 7 | name: service-controller 8 | namespace: kube-system 9 | roleRef: 10 | kind: ClusterRole 11 | name: cluster-admin 12 | apiGroup: rbac.authorization.k8s.io 13 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/consul/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f consul.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/consul/consul.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: consul 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: consul 10 | template: 11 | metadata: 12 | labels: 13 | app: consul 14 | spec: 15 | containers: 16 | - name: consul 17 | image: consul:latest 18 | ports: 19 | - containerPort: 8500 20 | name: http 21 | volumeMounts: 22 | - name: consul-data 23 | mountPath: /consul/data 24 | env: 25 | - name: CONSUL_BIND_INTERFACE 26 | value: eth0 27 | - name: CONSUL_LOCAL_CONFIG 28 | value: | 29 | { 30 | "datacenter": "dc1", 31 | "server": true, 32 | "ui": true, 33 | "bootstrap_expect": 1, 34 | "retry_join": ["provider=k8s label_selector=\"app=consul\""] 35 | } 36 | volumes: 37 | - name: consul-data 38 | persistentVolumeClaim: 39 | claimName: consul-data 40 | 41 | --- 42 | apiVersion: v1 43 | kind: Service 44 | metadata: 45 | name: consul 46 | spec: 47 | selector: 48 | app: consul 49 | ports: 50 | - port: 8500 51 | targetPort: 8500 52 | protocol: TCP 53 | nodePort: 30088 54 | type: NodePort 55 | 56 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/consul/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: consul-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/consul -------------------------------------------------------------------------------- /deployment/freecar-k8s/consul/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: consul-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi -------------------------------------------------------------------------------- /deployment/freecar-k8s/minio/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f minio.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/minio/minio.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: minio 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: minio 10 | template: 11 | metadata: 12 | labels: 13 | app: minio 14 | spec: 15 | containers: 16 | - name: minio 17 | image: quay.io/minio/minio:latest 18 | command: 19 | - /bin/bash 20 | - -c 21 | args: 22 | - minio server /data --console-address :9090 23 | ports: 24 | - containerPort: 9000 25 | - containerPort: 9090 26 | env: 27 | - name: MINIO_ACCESS_KEY 28 | value: minio 29 | - name: MINIO_SECRET_KEY 30 | value: password 31 | - name: MINIO_BROWSER 32 | value: "on" 33 | - name: MINIO_CONSOLE_UI 34 | value: "on" 35 | volumeMounts: 36 | - name: minio-data 37 | mountPath: /data 38 | volumes: 39 | - name: minio-data 40 | persistentVolumeClaim: 41 | claimName: minio-data 42 | --- 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | name: minio 47 | spec: 48 | type: NodePort 49 | selector: 50 | app: minio 51 | ports: 52 | - name: minio 53 | port: 9000 54 | targetPort: 9000 55 | - name: minio-ui 56 | port: 9090 57 | targetPort: 9090 58 | nodePort: 30087 -------------------------------------------------------------------------------- /deployment/freecar-k8s/minio/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: minio-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/minio -------------------------------------------------------------------------------- /deployment/freecar-k8s/minio/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: minio-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi -------------------------------------------------------------------------------- /deployment/freecar-k8s/mongodb/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f mongodb.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/mongodb/mongodb.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mongodb 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: mongodb 9 | replicas: 1 10 | template: 11 | metadata: 12 | labels: 13 | app: mongodb 14 | spec: 15 | containers: 16 | - name: mongodb 17 | image: mongo 18 | ports: 19 | - containerPort: 27017 20 | volumeMounts: 21 | - name: mongodb-data 22 | mountPath: /mongodb/data 23 | volumes: 24 | - name: mongodb-data 25 | persistentVolumeClaim: 26 | claimName: mongodb-data 27 | --- 28 | apiVersion: v1 29 | kind: Service 30 | metadata: 31 | name: mongodb 32 | spec: 33 | ports: 34 | - name: mongodb 35 | port: 27017 36 | targetPort: 27017 37 | nodePort: 30086 38 | type: NodePort 39 | selector: 40 | app: mongodb 41 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/mongodb/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: mongodb-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/mongodb -------------------------------------------------------------------------------- /deployment/freecar-k8s/mongodb/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: mongodb-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/mysql/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f mysql.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/mysql/mysql.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: mysql 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: mysql 9 | template: 10 | metadata: 11 | labels: 12 | app: mysql 13 | spec: 14 | containers: 15 | - name: mysql 16 | image: mysql:latest 17 | env: 18 | - name: MYSQL_ROOT_PASSWORD 19 | value: yourpassword 20 | ports: 21 | - containerPort: 3306 22 | name: mysql 23 | volumeMounts: 24 | - name: mysql-data 25 | mountPath: /mysql/data 26 | volumes: 27 | - name: mysql-data 28 | persistentVolumeClaim: 29 | claimName: mysql-data 30 | --- 31 | apiVersion: v1 32 | kind: Service 33 | metadata: 34 | name: mysql 35 | spec: 36 | selector: 37 | app: mysql 38 | ports: 39 | - port: 3306 40 | targetPort: 3306 41 | protocol: TCP 42 | nodePort: 30089 43 | type: NodePort 44 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/mysql/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: mysql-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/consul -------------------------------------------------------------------------------- /deployment/freecar-k8s/mysql/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: mysql-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/rabbitmq/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f rabbitmq.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/rabbitmq/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: rabbitmq-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/rabbitmq -------------------------------------------------------------------------------- /deployment/freecar-k8s/rabbitmq/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: rabbitmq-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/rabbitmq/rabbitmq.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: rabbitmq 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: rabbitmq 9 | replicas: 1 10 | template: 11 | metadata: 12 | labels: 13 | app: rabbitmq 14 | spec: 15 | volumes: 16 | - name: rabbitmq-data 17 | persistentVolumeClaim: 18 | claimName: rabbitmq-data 19 | containers: 20 | - name: rabbitmq 21 | image: rabbitmq:3-management 22 | ports: 23 | - containerPort: 5672 24 | - containerPort: 15672 25 | env: 26 | - name: RABBITMQ_DEFAULT_USER 27 | value: "admin" 28 | - name: RABBITMQ_DEFAULT_PASS 29 | value: "admin" 30 | volumeMounts: 31 | - name: rabbitmq-data 32 | mountPath: /rabbitmq/data 33 | --- 34 | apiVersion: v1 35 | kind: Service 36 | metadata: 37 | name: rabbitmq 38 | spec: 39 | ports: 40 | - name: amqp 41 | port: 5672 42 | targetPort: 5672 43 | nodePort: 30085 44 | - name: http 45 | port: 15672 46 | targetPort: 15672 47 | nodePort: 30084 48 | selector: 49 | app: rabbitmq 50 | type: NodePort 51 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/redis/Makefile: -------------------------------------------------------------------------------- 1 | make: 2 | kubectl apply -f pv.yaml 3 | kubectl apply -f pvc.yaml 4 | kubectl apply -f redis.yaml 5 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/redis/pv.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolume 3 | metadata: 4 | name: redis-data 5 | spec: 6 | capacity: 7 | storage: 1Gi 8 | accessModes: 9 | - ReadWriteOnce 10 | persistentVolumeReclaimPolicy: Retain 11 | hostPath: 12 | path: /mnt/data/redis -------------------------------------------------------------------------------- /deployment/freecar-k8s/redis/pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: redis-data 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | resources: 9 | requests: 10 | storage: 1Gi 11 | -------------------------------------------------------------------------------- /deployment/freecar-k8s/redis/redis.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: redis 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: redis 9 | replicas: 1 10 | template: 11 | metadata: 12 | labels: 13 | app: redis 14 | spec: 15 | containers: 16 | - name: redis 17 | image: redis:latest 18 | ports: 19 | - containerPort: 6379 20 | volumeMounts: 21 | - name: redis-data 22 | mountPath: /data 23 | volumes: 24 | - name: redis-data 25 | persistentVolumeClaim: 26 | claimName: redis-data 27 | --- 28 | apiVersion: v1 29 | kind: Service 30 | metadata: 31 | name: redis 32 | spec: 33 | ports: 34 | - name: redis 35 | port: 6379 36 | targetPort: 6379 37 | nodePort: 30083 38 | selector: 39 | app: redis 40 | type: NodePort -------------------------------------------------------------------------------- /img/FreeCar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/FreeCar.png -------------------------------------------------------------------------------- /img/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/back.png -------------------------------------------------------------------------------- /img/call_relation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/call_relation.png -------------------------------------------------------------------------------- /img/consul_kv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/consul_kv.png -------------------------------------------------------------------------------- /img/consul_service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/consul_service.png -------------------------------------------------------------------------------- /img/data-analize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/data-analize.png -------------------------------------------------------------------------------- /img/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/display.png -------------------------------------------------------------------------------- /img/jaeger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/jaeger.png -------------------------------------------------------------------------------- /img/jaeger2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/jaeger2.png -------------------------------------------------------------------------------- /img/minio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/minio.png -------------------------------------------------------------------------------- /img/pod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/pod.png -------------------------------------------------------------------------------- /img/prometheus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/prometheus.png -------------------------------------------------------------------------------- /img/pvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/pvc.png -------------------------------------------------------------------------------- /img/service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/service.png -------------------------------------------------------------------------------- /img/service_relations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/service_relations.png -------------------------------------------------------------------------------- /img/tech_arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CyanAsterisk/FreeCar/811da7b1ef5ce8cfcb904dd683e879ad74bc0c59/img/tech_arch.png -------------------------------------------------------------------------------- /licenses/LICENSE-amqp: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2019, Sean Treadway, SoundCloud Ltd. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /licenses/LICENSE-cos-go-skd-v5: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 mozillazg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/LICENSE-gorm: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-NOW Jinzhu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /licenses/LICENSE-jwt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Dave Grijalva 2 | Copyright (c) 2021 golang-jwt maintainers 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /licenses/LICENSE-lumberjack: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nate Finch 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /licenses/LICENSE-protobuf-go: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /licenses/LICENSE-snowflake: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Bruce 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /licenses/LICENSE-viper: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Steve Francia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /otel-collector-config.yaml: -------------------------------------------------------------------------------- 1 | receivers: 2 | otlp: 3 | protocols: 4 | grpc: 5 | 6 | exporters: 7 | prometheusremotewrite: 8 | endpoint: "http://victoriametrics:8428/api/v1/write" 9 | 10 | logging: 11 | 12 | jaeger: 13 | endpoint: jaeger-all-in-one:14250 14 | tls: 15 | insecure: true 16 | 17 | processors: 18 | batch: 19 | 20 | extensions: 21 | health_check: 22 | pprof: 23 | endpoint: :1888 24 | zpages: 25 | endpoint: :55679 26 | 27 | service: 28 | extensions: [ pprof, zpages, health_check ] 29 | pipelines: 30 | traces: 31 | receivers: [ otlp ] 32 | processors: [ batch ] 33 | exporters: [ logging, jaeger ] 34 | metrics: 35 | receivers: [ otlp ] 36 | processors: [ batch ] 37 | exporters: [ logging, prometheusremotewrite ] -------------------------------------------------------------------------------- /server/cmd/api/.hz: -------------------------------------------------------------------------------- 1 | // Code generated by hz. DO NOT EDIT. 2 | 3 | hz version: v0.6.6 4 | handlerDir: "" 5 | modelDir: "" 6 | routerDir: "" 7 | -------------------------------------------------------------------------------- /server/cmd/api/Makefile: -------------------------------------------------------------------------------- 1 | update: 2 | hz update -idl ./../../idl/http/$(service).thrift 3 | 4 | user: 5 | make service=user update 6 | car: 7 | make service=car update 8 | profile: 9 | make service=profile update 10 | trip: 11 | make service=trip update 12 | gpt: 13 | make service=gpt update 14 | 15 | all: 16 | make user 17 | make car 18 | make profile 19 | make trip 20 | make gpt -------------------------------------------------------------------------------- /server/cmd/api/biz/handler/ping.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package handler 4 | 5 | import ( 6 | "context" 7 | 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/common/utils" 10 | "github.com/cloudwego/hertz/pkg/protocol/consts" 11 | ) 12 | 13 | // Ping . 14 | func Ping(ctx context.Context, c *app.RequestContext) { 15 | c.JSON(consts.StatusOK, utils.H{ 16 | "message": "pong", 17 | }) 18 | } 19 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/car/car.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package car 4 | 5 | import ( 6 | car "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler/car" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.GET("/car", append(_getcarMw(), car.GetCar)...) 21 | root.GET("/cars", append(_getcarsMw(), car.GetCars)...) 22 | { 23 | _admin := root.Group("/admin", _adminMw()...) 24 | _admin.POST("/car", append(_admincreatecarMw(), car.AdminCreateCar)...) 25 | _car := _admin.Group("/car", _carMw()...) 26 | _car.GET("/all", append(_admingetallcarsMw(), car.AdminGetAllCars)...) 27 | _car.GET("/some", append(_admingetsomecarsMw(), car.AdminGetSomeCars)...) 28 | _admin.DELETE("/car", append(_admindeletecarMw(), car.AdminDeleteCar)...) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/car/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package car 4 | 5 | import ( 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/common" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | ) 10 | 11 | func rootMw() []app.HandlerFunc { 12 | return common.CommonMW() 13 | } 14 | 15 | func _adminMw() []app.HandlerFunc { 16 | return []app.HandlerFunc{ 17 | common.PasetoAuth(consts.Admin), 18 | } 19 | } 20 | 21 | func _carMw() []app.HandlerFunc { 22 | // your code... 23 | return nil 24 | } 25 | 26 | func _getcarMw() []app.HandlerFunc { 27 | return []app.HandlerFunc{ 28 | common.PasetoAuth(consts.User), 29 | } 30 | } 31 | 32 | func _getcarsMw() []app.HandlerFunc { 33 | return []app.HandlerFunc{ 34 | common.PasetoAuth(consts.User), 35 | } 36 | } 37 | 38 | func _admincreatecarMw() []app.HandlerFunc { 39 | // your code... 40 | return nil 41 | } 42 | 43 | func _admingetallcarsMw() []app.HandlerFunc { 44 | // your code... 45 | return nil 46 | } 47 | 48 | func _admingetsomecarsMw() []app.HandlerFunc { 49 | // your code... 50 | return nil 51 | } 52 | 53 | func _admindeletecarMw() []app.HandlerFunc { 54 | // your code... 55 | return nil 56 | } 57 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | pt "aidanwoods.dev/go-paseto" 8 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 11 | "github.com/CyanAsterisk/FreeCar/server/shared/middleware" 12 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 13 | "github.com/cloudwego/hertz/pkg/app" 14 | "github.com/cloudwego/hertz/pkg/common/hlog" 15 | "github.com/hertz-contrib/gzip" 16 | "github.com/hertz-contrib/paseto" 17 | ) 18 | 19 | func CommonMW() []app.HandlerFunc { 20 | return []app.HandlerFunc{ 21 | // use recovery mw 22 | middleware.Recovery(), 23 | // use gzip mw 24 | gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedExtensions([]string{".jpg", ".mp4", ".png"})), 25 | } 26 | } 27 | 28 | func PasetoAuth(audience string) app.HandlerFunc { 29 | pi := config.GlobalServerConfig.PasetoInfo 30 | pf, err := paseto.NewV4PublicParseFunc(pi.PubKey, []byte(pi.Implicit), paseto.WithAudience(audience), paseto.WithNotBefore()) 31 | if err != nil { 32 | hlog.Fatal(err) 33 | } 34 | sh := func(ctx context.Context, c *app.RequestContext, token *pt.Token) { 35 | aid, err := token.GetString("id") 36 | if err != nil { 37 | c.JSON(http.StatusUnauthorized, tools.BuildBaseResp(errno.BadRequest.WithMessage("missing accountID in token"))) 38 | c.Abort() 39 | return 40 | } 41 | c.Set(consts.AccountID, aid) 42 | } 43 | 44 | eh := func(ctx context.Context, c *app.RequestContext) { 45 | c.JSON(http.StatusUnauthorized, tools.BuildBaseResp(errno.BadRequest.WithMessage("invalid token"))) 46 | c.Abort() 47 | } 48 | return paseto.New(paseto.WithTokenPrefix("Bearer "), paseto.WithParseFunc(pf), paseto.WithSuccessHandler(sh), paseto.WithErrorFunc(eh)) 49 | } 50 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/gpt/gpt.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package gpt 4 | 5 | import ( 6 | gpt "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler/gpt" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.POST("/chat", append(_chatMw(), gpt.Chat)...) 21 | } 22 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/gpt/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package gpt 4 | 5 | import ( 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/common" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | ) 10 | 11 | func rootMw() []app.HandlerFunc { 12 | return common.CommonMW() 13 | } 14 | 15 | func _chatMw() []app.HandlerFunc { 16 | return []app.HandlerFunc{ 17 | common.PasetoAuth(consts.User), 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/profile/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package profile 4 | 5 | import ( 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/common" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | ) 10 | 11 | func rootMw() []app.HandlerFunc { 12 | return common.CommonMW() 13 | } 14 | 15 | func _profileMw() []app.HandlerFunc { 16 | return []app.HandlerFunc{ 17 | common.PasetoAuth(consts.User), 18 | } 19 | } 20 | 21 | func _adminMw() []app.HandlerFunc { 22 | return []app.HandlerFunc{ 23 | common.PasetoAuth(consts.Admin), 24 | } 25 | } 26 | 27 | func _checkprofileMw() []app.HandlerFunc { 28 | // your code... 29 | return nil 30 | } 31 | 32 | func _getpendingprofileMw() []app.HandlerFunc { 33 | // your code... 34 | return nil 35 | } 36 | 37 | func _getsomeprofileMw() []app.HandlerFunc { 38 | // your code... 39 | return nil 40 | } 41 | 42 | func _completeprofilephotoMw() []app.HandlerFunc { 43 | // your code... 44 | return nil 45 | } 46 | 47 | func _clearprofilephotoMw() []app.HandlerFunc { 48 | // your code... 49 | return nil 50 | } 51 | 52 | func _getprofilephotoMw() []app.HandlerFunc { 53 | // your code... 54 | return nil 55 | } 56 | 57 | func _createprofilephotoMw() []app.HandlerFunc { 58 | // your code... 59 | return nil 60 | } 61 | 62 | func _submitprofileMw() []app.HandlerFunc { 63 | return []app.HandlerFunc{ 64 | common.PasetoAuth(consts.User), 65 | } 66 | } 67 | 68 | func _clearprofileMw() []app.HandlerFunc { 69 | return []app.HandlerFunc{ 70 | common.PasetoAuth(consts.User), 71 | } 72 | } 73 | 74 | func _profile0Mw() []app.HandlerFunc { 75 | // your code... 76 | return nil 77 | } 78 | 79 | func _getprofileMw() []app.HandlerFunc { 80 | return []app.HandlerFunc{ 81 | common.PasetoAuth(consts.User), 82 | } 83 | } 84 | 85 | func _deleteprofileMw() []app.HandlerFunc { 86 | // your code... 87 | return nil 88 | } 89 | 90 | func _getallprofileMw() []app.HandlerFunc { 91 | // your code... 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/profile/profile.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package profile 4 | 5 | import ( 6 | profile "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler/profile" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.GET("/profile", append(_getprofileMw(), profile.GetProfile)...) 21 | _profile := root.Group("/profile", _profileMw()...) 22 | _profile.GET("/complete", append(_completeprofilephotoMw(), profile.CompleteProfilePhoto)...) 23 | _profile.POST("/photo", append(_createprofilephotoMw(), profile.CreateProfilePhoto)...) 24 | _profile.DELETE("/photo", append(_clearprofilephotoMw(), profile.ClearProfilePhoto)...) 25 | _profile.GET("/photo", append(_getprofilephotoMw(), profile.GetProfilePhoto)...) 26 | root.POST("/profile", append(_submitprofileMw(), profile.SubmitProfile)...) 27 | root.DELETE("/profile", append(_clearprofileMw(), profile.ClearProfile)...) 28 | { 29 | _admin := root.Group("/admin", _adminMw()...) 30 | _admin.DELETE("/profile", append(_deleteprofileMw(), profile.DeleteProfile)...) 31 | _profile0 := _admin.Group("/profile", _profile0Mw()...) 32 | _profile0.GET("/all", append(_getallprofileMw(), profile.GetAllProfile)...) 33 | _profile0.POST("/check", append(_checkprofileMw(), profile.CheckProfile)...) 34 | _profile0.GET("/pending", append(_getpendingprofileMw(), profile.GetPendingProfile)...) 35 | _profile0.GET("/some", append(_getsomeprofileMw(), profile.GetSomeProfile)...) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/register.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package router 4 | 5 | import ( 6 | car "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/car" 7 | gpt "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/gpt" 8 | profile "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/profile" 9 | trip "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/trip" 10 | user "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/user" 11 | "github.com/cloudwego/hertz/pkg/app/server" 12 | ) 13 | 14 | // GeneratedRegister registers routers generated by IDL. 15 | func GeneratedRegister(r *server.Hertz) { 16 | //INSERT_POINT: DO NOT DELETE THIS LINE! 17 | gpt.Register(r) 18 | 19 | trip.Register(r) 20 | 21 | profile.Register(r) 22 | 23 | car.Register(r) 24 | 25 | user.Register(r) 26 | 27 | } 28 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/trip/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package trip 4 | 5 | import ( 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/common" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | ) 10 | 11 | func rootMw() []app.HandlerFunc { 12 | return common.CommonMW() 13 | } 14 | 15 | func _adminMw() []app.HandlerFunc { 16 | return []app.HandlerFunc{ 17 | common.PasetoAuth(consts.Admin), 18 | } 19 | } 20 | 21 | func _tripMw() []app.HandlerFunc { 22 | // your code... 23 | return nil 24 | } 25 | 26 | func _get_lltripsMw() []app.HandlerFunc { 27 | // your code... 28 | return nil 29 | } 30 | 31 | func _getsometripsMw() []app.HandlerFunc { 32 | // your code... 33 | return nil 34 | } 35 | 36 | func _createtripMw() []app.HandlerFunc { 37 | return []app.HandlerFunc{ 38 | common.PasetoAuth(consts.User), 39 | } 40 | } 41 | 42 | func _gettripMw() []app.HandlerFunc { 43 | return []app.HandlerFunc{ 44 | common.PasetoAuth(consts.User), 45 | } 46 | } 47 | 48 | func _gettripsMw() []app.HandlerFunc { 49 | return []app.HandlerFunc{ 50 | common.PasetoAuth(consts.User), 51 | } 52 | } 53 | 54 | func _updatetripMw() []app.HandlerFunc { 55 | return []app.HandlerFunc{ 56 | common.PasetoAuth(consts.User), 57 | } 58 | } 59 | 60 | func _deletetripMw() []app.HandlerFunc { 61 | // your code... 62 | return nil 63 | } 64 | 65 | func _getalltripsMw() []app.HandlerFunc { 66 | // your code... 67 | return nil 68 | } 69 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/trip/trip.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package trip 4 | 5 | import ( 6 | trip "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler/trip" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | root.POST("/trip", append(_createtripMw(), trip.CreateTrip)...) 21 | root.GET("/trip", append(_gettripMw(), trip.GetTrip)...) 22 | root.PUT("/trip", append(_updatetripMw(), trip.UpdateTrip)...) 23 | root.GET("/trips", append(_gettripsMw(), trip.GetTrips)...) 24 | { 25 | _admin := root.Group("/admin", _adminMw()...) 26 | _admin.DELETE("/trip", append(_deletetripMw(), trip.DeleteTrip)...) 27 | _trip := _admin.Group("/trip", _tripMw()...) 28 | _trip.GET("/all", append(_getalltripsMw(), trip.GetAllTrips)...) 29 | _trip.GET("/some", append(_getsometripsMw(), trip.GetSomeTrips)...) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/user/middleware.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package user 4 | 5 | import ( 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router/common" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | ) 10 | 11 | func rootMw() []app.HandlerFunc { 12 | return common.CommonMW() 13 | } 14 | 15 | func _adminMw() []app.HandlerFunc { 16 | return []app.HandlerFunc{ 17 | common.PasetoAuth(consts.Admin), 18 | } 19 | } 20 | 21 | func _userMw() []app.HandlerFunc { 22 | // your code... 23 | return nil 24 | } 25 | 26 | func _loginMw() []app.HandlerFunc { 27 | // your code... 28 | return nil 29 | } 30 | 31 | func _login0Mw() []app.HandlerFunc { 32 | // your code... 33 | return nil 34 | } 35 | 36 | func _getuserinfoMw() []app.HandlerFunc { 37 | // your code... 38 | return nil 39 | } 40 | 41 | func _passwordMw() []app.HandlerFunc { 42 | return []app.HandlerFunc{ 43 | common.PasetoAuth(consts.Admin), 44 | } 45 | } 46 | 47 | func _user0Mw() []app.HandlerFunc { 48 | return []app.HandlerFunc{ 49 | common.PasetoAuth(consts.User), 50 | } 51 | } 52 | 53 | func _updateuserinfoMw() []app.HandlerFunc { 54 | // your code... 55 | return nil 56 | } 57 | 58 | func _adminadduserMw() []app.HandlerFunc { 59 | // your code... 60 | return nil 61 | } 62 | 63 | func _admingetallusersMw() []app.HandlerFunc { 64 | // your code... 65 | return nil 66 | } 67 | 68 | func _admingetsomeusersMw() []app.HandlerFunc { 69 | // your code... 70 | return nil 71 | } 72 | 73 | func _admindeleteuserMw() []app.HandlerFunc { 74 | // your code... 75 | return nil 76 | } 77 | 78 | func _adminupdateuserMw() []app.HandlerFunc { 79 | // your code... 80 | return nil 81 | } 82 | 83 | func _adminloginMw() []app.HandlerFunc { 84 | // your code... 85 | return nil 86 | } 87 | 88 | func _adminchangepasswordMw() []app.HandlerFunc { 89 | // your code... 90 | return nil 91 | } 92 | 93 | func _uploadavatarMw() []app.HandlerFunc { 94 | // your code... 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /server/cmd/api/biz/router/user/user.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package user 4 | 5 | import ( 6 | user "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler/user" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | /* 11 | This file will register all the routes of the services in the master idl. 12 | And it will update automatically when you use the "update" command for the idl. 13 | So don't modify the contents of the file, or your code will be deleted when it is updated. 14 | */ 15 | 16 | // Register register routes based on the IDL 'api.${HTTP Method}' annotation. 17 | func Register(r *server.Hertz) { 18 | 19 | root := r.Group("/", rootMw()...) 20 | { 21 | _admin := root.Group("/admin", _adminMw()...) 22 | _admin.POST("/user", append(_adminadduserMw(), user.AdminAddUser)...) 23 | _user := _admin.Group("/user", _userMw()...) 24 | _user.GET("/all", append(_admingetallusersMw(), user.AdminGetAllUsers)...) 25 | _user.GET("/some", append(_admingetsomeusersMw(), user.AdminGetSomeUsers)...) 26 | _admin.DELETE("/user", append(_admindeleteuserMw(), user.AdminDeleteUser)...) 27 | _admin.PUT("/user", append(_adminupdateuserMw(), user.AdminUpdateUser)...) 28 | } 29 | { 30 | _login := root.Group("/login", _loginMw()...) 31 | _login.POST("/admin", append(_adminloginMw(), user.AdminLogin)...) 32 | _login.POST("/user", append(_login0Mw(), user.Login)...) 33 | } 34 | { 35 | _password := root.Group("/password", _passwordMw()...) 36 | _password.POST("/admin", append(_adminchangepasswordMw(), user.AdminChangePassword)...) 37 | } 38 | { 39 | _user0 := root.Group("/user", _user0Mw()...) 40 | _user0.POST("/avatar", append(_uploadavatarMw(), user.UploadAvatar)...) 41 | _user0.GET("/info", append(_getuserinfoMw(), user.GetUserInfo)...) 42 | _user0.PUT("/info", append(_updateuserinfoMw(), user.UpdateUserInfo)...) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/api/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/api_srv' -------------------------------------------------------------------------------- /server/cmd/api/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type PasetoConfig struct { 4 | PubKey string `mapstructure:"pub_key" json:"pub_key"` 5 | Implicit string `mapstructure:"implicit" json:"implicit"` 6 | } 7 | 8 | type ConsulConfig struct { 9 | Host string `mapstructure:"host" json:"host"` 10 | Port int `mapstructure:"port" json:"port"` 11 | Key string `mapstructure:"key" json:"key"` 12 | } 13 | 14 | type OtelConfig struct { 15 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 16 | } 17 | 18 | type ServerConfig struct { 19 | Name string `mapstructure:"name" json:"name"` 20 | Host string `mapstructure:"host" json:"host"` 21 | Port int `mapstructure:"port" json:"port"` 22 | ChatToken string `mapstructure:"chat_token" json:"chat_token"` 23 | ProxyURL string `mapstructure:"proxy" json:"proxy"` 24 | PasetoInfo PasetoConfig `mapstructure:"paseto" json:"paseto"` 25 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 26 | UserSrvInfo RPCSrvConfig `mapstructure:"user_srv" json:"user_srv"` 27 | CarSrvInfo RPCSrvConfig `mapstructure:"car_srv" json:"car_srv"` 28 | ProfileSrvInfo RPCSrvConfig `mapstructure:"profile_srv" json:"profile_srv"` 29 | TripSrvInfo RPCSrvConfig `mapstructure:"trip_srv" json:"trip_srv"` 30 | } 31 | 32 | type RPCSrvConfig struct { 33 | Name string `mapstructure:"name" json:"name"` 34 | } 35 | -------------------------------------------------------------------------------- /server/cmd/api/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 5 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 6 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip/tripservice" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/user/userservice" 8 | ) 9 | 10 | var ( 11 | GlobalServerConfig ServerConfig 12 | GlobalConsulConfig ConsulConfig 13 | 14 | GlobalUserClient userservice.Client 15 | GlobalCarClient carservice.Client 16 | GlobalProfileClient profileservice.Client 17 | GlobalTripClient tripservice.Client 18 | ) 19 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.ApiConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/cors.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/hertz-contrib/cors" 8 | ) 9 | 10 | // InitCors return cors.Config. 11 | func InitCors() cors.Config { 12 | return cors.Config{ 13 | AllowOrigins: []string{consts.CorsAddress}, 14 | AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"}, 15 | AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization", "Token", "Accept"}, 16 | AllowCredentials: true, 17 | MaxAge: 12 * time.Hour, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/cloudwego/hertz/pkg/common/hlog" 11 | hertzlogrus "github.com/hertz-contrib/obs-opentelemetry/logging/logrus" 12 | "gopkg.in/natefinch/lumberjack.v2" 13 | ) 14 | 15 | // InitLogger to init logrus 16 | func InitLogger() { 17 | // Customizable output directory. 18 | logFilePath := consts.HlogFilePath 19 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 20 | panic(err) 21 | } 22 | 23 | // Set filename to date 24 | logFileName := time.Now().Format("2006-01-02") + ".log" 25 | fileName := path.Join(logFilePath, logFileName) 26 | if _, err := os.Stat(fileName); err != nil { 27 | if _, err := os.Create(fileName); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | logger := hertzlogrus.NewLogger() 33 | // Provides compression and deletion 34 | lumberjackLogger := &lumberjack.Logger{ 35 | Filename: fileName, 36 | MaxSize: 20, // A file can be up to 20M. 37 | MaxBackups: 5, // Save up to 5 files at the same time. 38 | MaxAge: 10, // A file can exist for a maximum of 10 days. 39 | Compress: true, // Compress with gzip. 40 | } 41 | if runtime.GOOS == "linux" { 42 | logger.SetOutput(lumberjackLogger) 43 | logger.SetLevel(hlog.LevelWarn) 44 | } else { 45 | logger.SetLevel(hlog.LevelDebug) 46 | } 47 | 48 | hlog.SetLogger(logger) 49 | } 50 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/hertz/pkg/app/server/registry" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/cloudwego/hertz/pkg/common/utils" 13 | "github.com/hashicorp/consul/api" 14 | "github.com/hertz-contrib/registry/consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry() (registry.Registry, *registry.Info) { 19 | // build a consul client 20 | cfg := api.DefaultConfig() 21 | cfg.Address = net.JoinHostPort( 22 | config.GlobalConsulConfig.Host, 23 | strconv.Itoa(config.GlobalConsulConfig.Port)) 24 | consulClient, err := api.NewClient(cfg) 25 | if err != nil { 26 | hlog.Fatalf("new consul client failed: %s", err.Error()) 27 | } 28 | 29 | r := consul.NewConsulRegister(consulClient, 30 | consul.WithCheck(&api.AgentServiceCheck{ 31 | Interval: consts.ConsulCheckInterval, 32 | Timeout: consts.ConsulCheckTimeout, 33 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 34 | })) 35 | 36 | // Using snowflake to generate service name. 37 | sf, err := snowflake.NewNode(2) 38 | if err != nil { 39 | hlog.Fatalf("generate service name failed: %s", err.Error()) 40 | } 41 | info := ®istry.Info{ 42 | ServiceName: config.GlobalServerConfig.Name, 43 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, 44 | strconv.Itoa(config.GlobalServerConfig.Port))), 45 | Tags: map[string]string{ 46 | "ID": sf.Generate().Base36(), 47 | }, 48 | Weight: registry.DefaultWeight, 49 | } 50 | return r, info 51 | } 52 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/rpc/car_service.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/cloudwego/kitex/pkg/loadbalance" 11 | "github.com/cloudwego/kitex/pkg/rpcinfo" 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | func initCar() { 18 | // init resolver 19 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 20 | config.GlobalConsulConfig.Host, 21 | config.GlobalConsulConfig.Port)) 22 | if err != nil { 23 | klog.Fatalf("new consul client failed: %s", err.Error()) 24 | } 25 | // init OpenTelemetry 26 | provider.NewOpenTelemetryProvider( 27 | provider.WithServiceName(config.GlobalServerConfig.CarSrvInfo.Name), 28 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 29 | provider.WithInsecure(), 30 | ) 31 | 32 | // create a new client 33 | c, err := carservice.NewClient( 34 | config.GlobalServerConfig.CarSrvInfo.Name, 35 | client.WithResolver(r), // service discovery 36 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 37 | client.WithMuxConnection(1), // multiplexing 38 | client.WithSuite(tracing.NewClientSuite()), 39 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.CarSrvInfo.Name}), 40 | ) 41 | if err != nil { 42 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 43 | } 44 | config.GlobalCarClient = c 45 | } 46 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/rpc/init.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // Init rpc service 4 | func Init() { 5 | initUser() 6 | initCar() 7 | initProfile() 8 | initTrip() 9 | } 10 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/rpc/profile_service.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/cloudwego/kitex/pkg/loadbalance" 11 | "github.com/cloudwego/kitex/pkg/rpcinfo" 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | func initProfile() { 18 | // init resolver 19 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 20 | config.GlobalConsulConfig.Host, 21 | config.GlobalConsulConfig.Port)) 22 | if err != nil { 23 | klog.Fatalf("new consul client failed: %s", err.Error()) 24 | } 25 | // init OpenTelemetry 26 | provider.NewOpenTelemetryProvider( 27 | provider.WithServiceName(config.GlobalServerConfig.ProfileSrvInfo.Name), 28 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 29 | provider.WithInsecure(), 30 | ) 31 | 32 | // create a new client 33 | c, err := profileservice.NewClient( 34 | config.GlobalServerConfig.ProfileSrvInfo.Name, 35 | client.WithResolver(r), // service discovery 36 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 37 | client.WithMuxConnection(1), // multiplexing 38 | client.WithSuite(tracing.NewClientSuite()), 39 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.ProfileSrvInfo.Name}), 40 | ) 41 | if err != nil { 42 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 43 | } 44 | config.GlobalProfileClient = c 45 | } 46 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/rpc/trip_service.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip/tripservice" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/cloudwego/kitex/pkg/loadbalance" 11 | "github.com/cloudwego/kitex/pkg/rpcinfo" 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | func initTrip() { 18 | // init resolver 19 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 20 | config.GlobalConsulConfig.Host, 21 | config.GlobalConsulConfig.Port)) 22 | if err != nil { 23 | klog.Fatalf("new consul client failed: %s", err.Error()) 24 | } 25 | // init OpenTelemetry 26 | provider.NewOpenTelemetryProvider( 27 | provider.WithServiceName(config.GlobalServerConfig.TripSrvInfo.Name), 28 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 29 | provider.WithInsecure(), 30 | ) 31 | 32 | // create a new client 33 | c, err := tripservice.NewClient( 34 | config.GlobalServerConfig.TripSrvInfo.Name, 35 | client.WithResolver(r), // service discovery 36 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 37 | client.WithMuxConnection(1), // multiplexing 38 | client.WithSuite(tracing.NewClientSuite()), 39 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.TripSrvInfo.Name}), 40 | ) 41 | if err != nil { 42 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 43 | } 44 | config.GlobalTripClient = c 45 | } 46 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/rpc/user_service.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/user/userservice" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/cloudwego/kitex/pkg/loadbalance" 11 | "github.com/cloudwego/kitex/pkg/rpcinfo" 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | func initUser() { 18 | // init resolver 19 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 20 | config.GlobalConsulConfig.Host, 21 | config.GlobalConsulConfig.Port)) 22 | if err != nil { 23 | klog.Fatalf("new consul client failed: %s", err.Error()) 24 | } 25 | // init OpenTelemetry 26 | provider.NewOpenTelemetryProvider( 27 | provider.WithServiceName(config.GlobalServerConfig.UserSrvInfo.Name), 28 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 29 | provider.WithInsecure(), 30 | ) 31 | 32 | // create a new client 33 | c, err := userservice.NewClient( 34 | config.GlobalServerConfig.UserSrvInfo.Name, 35 | client.WithResolver(r), // service discovery 36 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 37 | client.WithMuxConnection(1), // multiplexing 38 | client.WithSuite(tracing.NewClientSuite()), 39 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.UserSrvInfo.Name}), 40 | ) 41 | if err != nil { 42 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 43 | } 44 | config.GlobalUserClient = c 45 | } 46 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/sentinel.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 5 | sentinel "github.com/alibaba/sentinel-golang/api" 6 | "github.com/alibaba/sentinel-golang/core/flow" 7 | "github.com/cloudwego/hertz/pkg/common/hlog" 8 | ) 9 | 10 | func InitSentinel() { 11 | err := sentinel.InitDefault() 12 | if err != nil { 13 | hlog.Fatal("init sentinel failed", err) 14 | } 15 | _, err = flow.LoadRules([]*flow.Rule{ 16 | { 17 | Resource: consts.FreeCar, 18 | Threshold: 10, 19 | TokenCalculateStrategy: flow.WarmUp, 20 | ControlBehavior: flow.Throttling, 21 | StatIntervalInMs: 1000, 22 | }, 23 | }) 24 | if err != nil { 25 | hlog.Fatal("load sentinel failed", err) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/cmd/api/initialize/tls.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "crypto/tls" 5 | 6 | "github.com/cloudwego/hertz/pkg/common/hlog" 7 | ) 8 | 9 | // InitTLS 10 | func InitTLS() *tls.Config { 11 | cfg := &tls.Config{ 12 | MinVersion: tls.VersionTLS10, 13 | InsecureSkipVerify: true, 14 | } 15 | cert, err := tls.LoadX509KeyPair("server/cmd/api/cert/server.crt", 16 | "server/cmd/api/cert/server.key") 17 | if err != nil { 18 | hlog.Fatal("tls failed", err) 19 | } 20 | cfg.Certificates = append(cfg.Certificates, cert) 21 | return cfg 22 | } 23 | -------------------------------------------------------------------------------- /server/cmd/api/main.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "fmt" 8 | "net/http" 9 | 10 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/config" 11 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/initialize" 12 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/initialize/rpc" 13 | "github.com/cloudwego/hertz/pkg/app" 14 | "github.com/cloudwego/hertz/pkg/app/server" 15 | "github.com/hertz-contrib/cors" 16 | hertztracing "github.com/hertz-contrib/obs-opentelemetry/tracing" 17 | hertzSentinel "github.com/hertz-contrib/opensergo/sentinel/adapter" 18 | "github.com/hertz-contrib/pprof" 19 | ) 20 | 21 | func main() { 22 | // initialize 23 | initialize.InitLogger() 24 | initialize.InitConfig() 25 | r, info := initialize.InitRegistry() 26 | initialize.InitSentinel() 27 | tracer, trcCfg := hertztracing.NewServerTracer() 28 | corsCfg := initialize.InitCors() 29 | rpc.Init() 30 | // create a new server 31 | h := server.New( 32 | tracer, 33 | server.WithALPN(true), 34 | server.WithHostPorts(fmt.Sprintf(":%d", config.GlobalServerConfig.Port)), 35 | server.WithRegistry(r, info), 36 | server.WithHandleMethodNotAllowed(true), 37 | ) 38 | 39 | // use pprof & tracer & sentinel 40 | pprof.Register(h) 41 | h.Use(cors.New(corsCfg)) 42 | h.Use(hertztracing.ServerMiddleware(trcCfg)) 43 | h.Use(hertzSentinel.SentinelServerMiddleware( 44 | // abort with status 429 by default 45 | hertzSentinel.WithServerBlockFallback(func(c context.Context, ctx *app.RequestContext) { 46 | ctx.JSON(http.StatusTooManyRequests, nil) 47 | ctx.Abort() 48 | }), 49 | )) 50 | register(h) 51 | h.Spin() 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/api/pkg/convert.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | hbase "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/model/base" 5 | kbase "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 6 | ) 7 | 8 | func ConvertTripLocation(l *hbase.Location) *kbase.Location { 9 | return &kbase.Location{ 10 | Latitude: l.Latitude, 11 | Longitude: l.Longitude, 12 | } 13 | } 14 | 15 | func ConvertDriver(d *hbase.Driver) *kbase.Driver { 16 | if d == nil { 17 | return nil 18 | } 19 | return &kbase.Driver{ 20 | Id: d.ID, 21 | AvatarUrl: d.AvatarURL, 22 | } 23 | } 24 | 25 | func ConvertCarLocation(l *hbase.Position) *kbase.Position { 26 | if l == nil { 27 | return nil 28 | } 29 | return &kbase.Position{ 30 | Latitude: l.Latitude, 31 | Longitude: l.Longitude, 32 | } 33 | } 34 | 35 | func ConvertCar(c *hbase.Car) *kbase.Car { 36 | if c == nil { 37 | return nil 38 | } 39 | return &kbase.Car{ 40 | Status: kbase.CarStatus(c.Status), 41 | Driver: ConvertDriver(c.Driver), 42 | Position: ConvertCarLocation(c.Position), 43 | TripId: c.TripID, 44 | Power: c.Power, 45 | PlateNum: c.PlateNum, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /server/cmd/api/router.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. 2 | 3 | package main 4 | 5 | import ( 6 | "context" 7 | "net/http" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/handler" 10 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 11 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 12 | "github.com/cloudwego/hertz/pkg/app" 13 | "github.com/cloudwego/hertz/pkg/app/server" 14 | ) 15 | 16 | // customizeRegister registers customize routers. 17 | func customizedRegister(r *server.Hertz) { 18 | r.GET("/ping", handler.Ping) 19 | 20 | // your code ... 21 | r.NoRoute(func(ctx context.Context, c *app.RequestContext) { // used for HTTP 404 22 | c.JSON(http.StatusNotFound, tools.BuildBaseResp(errno.NoRoute)) 23 | }) 24 | r.NoMethod(func(ctx context.Context, c *app.RequestContext) { // used for HTTP 405 25 | c.JSON(http.StatusMethodNotAllowed, tools.BuildBaseResp(errno.NoMethod)) 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /server/cmd/api/router_gen.go: -------------------------------------------------------------------------------- 1 | // Code generated by hertz generator. DO NOT EDIT. 2 | 3 | package main 4 | 5 | import ( 6 | router "github.com/CyanAsterisk/FreeCar/server/cmd/api/biz/router" 7 | "github.com/cloudwego/hertz/pkg/app/server" 8 | ) 9 | 10 | // register registers all routers. 11 | func register(r *server.Hertz) { 12 | 13 | router.GeneratedRegister(r) 14 | 15 | customizedRegister(r) 16 | } 17 | -------------------------------------------------------------------------------- /server/cmd/blob/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | kitex -service blob -module github.com/CyanAsterisk/FreeCar -use github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen ./../../idl/rpc/blob.thrift 3 | rm build.sh 4 | rm -rf script -------------------------------------------------------------------------------- /server/cmd/blob/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/blob_srv' -------------------------------------------------------------------------------- /server/cmd/blob/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type MysqlConfig struct { 4 | Host string `mapstructure:"host" json:"host"` 5 | Port int `mapstructure:"port" json:"port"` 6 | Name string `mapstructure:"db" json:"db"` 7 | User string `mapstructure:"user" json:"user"` 8 | Password string `mapstructure:"password" json:"password"` 9 | Salt string `mapstructure:"salt" json:"salt"` 10 | } 11 | 12 | type ConsulConfig struct { 13 | Host string `mapstructure:"host" json:"host"` 14 | Port int `mapstructure:"port" json:"port"` 15 | Key string `mapstructure:"key" json:"key"` 16 | } 17 | 18 | type OtelConfig struct { 19 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 20 | } 21 | 22 | type MinioConfig struct { 23 | Endpoint string `mapstructure:"endpoint" json:"endpoint"` 24 | AccessKeyID string `mapstructure:"access_key_id" json:"access_key_id"` 25 | SecretAccessKey string `mapstructure:"secret_access_key" json:"secret_access_key"` 26 | Bucket string `mapstructure:"bucket" json:"bucket"` 27 | } 28 | 29 | type ServerConfig struct { 30 | Name string `mapstructure:"name" json:"name"` 31 | Host string `mapstructure:"host" json:"host"` 32 | MysqlInfo MysqlConfig `mapstructure:"mysql" json:"mysql"` 33 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 34 | MinioInfo MinioConfig `mapstructure:"minio" json:"minio"` 35 | } 36 | -------------------------------------------------------------------------------- /server/cmd/blob/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | var ( 4 | GlobalServerConfig ServerConfig 5 | GlobalConsulConfig ConsulConfig 6 | ) 7 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.BlobConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/db.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "gorm.io/driver/mysql" 11 | "gorm.io/gorm" 12 | "gorm.io/gorm/logger" 13 | "gorm.io/gorm/schema" 14 | "gorm.io/plugin/opentelemetry/logging/logrus" 15 | "gorm.io/plugin/opentelemetry/tracing" 16 | ) 17 | 18 | // InitDB to init database 19 | func InitDB() *gorm.DB { 20 | c := config.GlobalServerConfig.MysqlInfo 21 | dsn := fmt.Sprintf(consts.MySqlDSN, c.User, c.Password, c.Host, c.Port, c.Name) 22 | newLogger := logger.New( 23 | logrus.NewWriter(), // io writer 24 | logger.Config{ 25 | SlowThreshold: time.Second, // Slow SQL Threshold 26 | LogLevel: logger.Silent, // Log level 27 | Colorful: true, // Disable color printing 28 | }, 29 | ) 30 | 31 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ 32 | NamingStrategy: schema.NamingStrategy{ 33 | SingularTable: true, 34 | }, 35 | Logger: newLogger, 36 | }) 37 | if err != nil { 38 | klog.Fatalf("init gorm failed: %s", err.Error()) 39 | } 40 | if err := db.Use(tracing.NewPlugin()); err != nil { 41 | klog.Fatalf("use tracing plugin failed: %s", err.Error()) 42 | } 43 | return db 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/flag.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | // InitFlag to init flag 12 | func InitFlag() (string, int) { 13 | IP := flag.String(consts.IPFlagName, consts.IPFlagValue, consts.IPFlagUsage) 14 | Port := flag.Int(consts.PortFlagName, 0, consts.PortFlagUsage) 15 | // Parsing flags and if Port is 0 , then will automatically get an empty Port. 16 | flag.Parse() 17 | if *Port == 0 { 18 | *Port, _ = tools.GetFreePort() 19 | } 20 | klog.Info("ip: ", *IP) 21 | klog.Info("port: ", *Port) 22 | return *IP, *Port 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus" 12 | "gopkg.in/natefinch/lumberjack.v2" 13 | ) 14 | 15 | // InitLogger to init logrus 16 | func InitLogger() { 17 | // Customizable output directory. 18 | logFilePath := consts.KlogFilePath 19 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 20 | panic(err) 21 | } 22 | 23 | // Set filename to date 24 | logFileName := time.Now().Format("2006-01-02") + ".log" 25 | fileName := path.Join(logFilePath, logFileName) 26 | if _, err := os.Stat(fileName); err != nil { 27 | if _, err := os.Create(fileName); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | logger := kitexlogrus.NewLogger() 33 | // Provides compression and deletion 34 | lumberjackLogger := &lumberjack.Logger{ 35 | Filename: fileName, 36 | MaxSize: 20, // A file can be up to 20M. 37 | MaxBackups: 5, // Save up to 5 files at the same time. 38 | MaxAge: 10, // A file can exist for a maximum of 10 days. 39 | Compress: true, // Compress with gzip. 40 | } 41 | 42 | if runtime.GOOS == "linux" { 43 | logger.SetOutput(lumberjackLogger) 44 | logger.SetLevel(klog.LevelWarn) 45 | } else { 46 | logger.SetLevel(klog.LevelDebug) 47 | } 48 | 49 | klog.SetLogger(logger) 50 | } 51 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/minio.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/config" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | "github.com/minio/minio-go/v7" 9 | "github.com/minio/minio-go/v7/pkg/credentials" 10 | ) 11 | 12 | func InitMinio() *minio.Client { 13 | mi := config.GlobalServerConfig.MinioInfo 14 | // Initialize minio client object. 15 | mc, err := minio.New(mi.Endpoint, &minio.Options{ 16 | Creds: credentials.NewStaticV4(mi.AccessKeyID, mi.SecretAccessKey, ""), 17 | Secure: false, 18 | }) 19 | if err != nil { 20 | klog.Fatalf("create minio client err: %s", err.Error()) 21 | } 22 | exists, err := mc.BucketExists(context.Background(), mi.Bucket) 23 | if err != nil { 24 | klog.Fatal(err) 25 | } 26 | if !exists { 27 | err = mc.MakeBucket(context.Background(), mi.Bucket, minio.MakeBucketOptions{Region: "cn-north-1"}) 28 | if err != nil { 29 | klog.Fatalf("make bucket err: %s", err.Error()) 30 | } 31 | } 32 | policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::` + mi.Bucket + `/*"],"Sid": ""}]}` 33 | err = mc.SetBucketPolicy(context.Background(), mi.Bucket, policy) 34 | if err != nil { 35 | klog.Fatal("set bucket policy err:%s", err) 36 | } 37 | return mc 38 | } 39 | -------------------------------------------------------------------------------- /server/cmd/blob/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/registry" 12 | "github.com/cloudwego/kitex/pkg/utils" 13 | "github.com/hashicorp/consul/api" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry(Port int) (registry.Registry, *registry.Info) { 19 | r, err := consul.NewConsulRegister(net.JoinHostPort( 20 | config.GlobalConsulConfig.Host, 21 | strconv.Itoa(config.GlobalConsulConfig.Port)), 22 | consul.WithCheck(&api.AgentServiceCheck{ 23 | Interval: consts.ConsulCheckInterval, 24 | Timeout: consts.ConsulCheckTimeout, 25 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 26 | })) 27 | if err != nil { 28 | klog.Fatalf("new consul register failed: %s", err.Error()) 29 | } 30 | 31 | // Using snowflake to generate service name. 32 | sf, err := snowflake.NewNode(2) 33 | if err != nil { 34 | klog.Fatalf("generate service name failed: %s", err.Error()) 35 | } 36 | info := ®istry.Info{ 37 | ServiceName: config.GlobalServerConfig.Name, 38 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, strconv.Itoa(Port))), 39 | Tags: map[string]string{ 40 | "ID": sf.Generate().Base36(), 41 | }, 42 | } 43 | return r, info 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/blob/kitex.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'blob' 3 | ToolVersion: 'v0.4.4' 4 | -------------------------------------------------------------------------------- /server/cmd/blob/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/config" 9 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/initialize" 10 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/pkg/minio" 11 | "github.com/CyanAsterisk/FreeCar/server/cmd/blob/pkg/mysql" 12 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 13 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob/blobservice" 14 | "github.com/cloudwego/kitex/pkg/klog" 15 | "github.com/cloudwego/kitex/pkg/limit" 16 | "github.com/cloudwego/kitex/pkg/rpcinfo" 17 | "github.com/cloudwego/kitex/pkg/utils" 18 | "github.com/cloudwego/kitex/server" 19 | "github.com/kitex-contrib/obs-opentelemetry/provider" 20 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 21 | ) 22 | 23 | func main() { 24 | // initialization 25 | initialize.InitLogger() 26 | initialize.InitConfig() 27 | IP, Port := initialize.InitFlag() 28 | r, info := initialize.InitRegistry(Port) 29 | db := initialize.InitDB() 30 | minioClient := initialize.InitMinio() 31 | p := provider.NewOpenTelemetryProvider( 32 | provider.WithServiceName(config.GlobalServerConfig.Name), 33 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 34 | provider.WithInsecure(), 35 | ) 36 | defer p.Shutdown(context.Background()) 37 | 38 | // Create new server. 39 | srv := blobservice.NewServer(&BlobServiceImpl{ 40 | minioManager: minio.NewManager(minioClient, config.GlobalServerConfig.MinioInfo.Bucket), 41 | mysqlManager: mysql.NewManager(db), 42 | }, 43 | server.WithServiceAddr(utils.NewNetAddr(consts.TCP, net.JoinHostPort(IP, strconv.Itoa(Port)))), 44 | server.WithRegistry(r), 45 | server.WithRegistryInfo(info), 46 | server.WithLimit(&limit.Option{MaxConnections: 2000, MaxQPS: 500}), 47 | server.WithSuite(tracing.NewServerSuite()), 48 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.Name}), 49 | ) 50 | 51 | err := srv.Run() 52 | if err != nil { 53 | klog.Fatal(err) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /server/cmd/blob/pkg/minio/minio.go: -------------------------------------------------------------------------------- 1 | package minio 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/minio/minio-go/v7" 8 | ) 9 | 10 | type Manager struct { 11 | bucketName string 12 | client *minio.Client 13 | } 14 | 15 | func NewManager(client *minio.Client, bucketName string) *Manager { 16 | return &Manager{bucketName: bucketName, client: client} 17 | } 18 | 19 | func (s *Manager) GetObjectURL(ctx context.Context, objectName string, timeOut time.Duration) (string, error) { 20 | url, err := s.client.PresignedGetObject(ctx, s.bucketName, objectName, timeOut, nil) 21 | if err != nil { 22 | return "", err 23 | } 24 | return url.String(), err 25 | } 26 | 27 | func (s *Manager) PutObjectURL(ctx context.Context, objectName string, timeOut time.Duration) (string, error) { 28 | url, err := s.client.PresignedPutObject(ctx, s.bucketName, objectName, timeOut) 29 | if err != nil { 30 | return "", err 31 | } 32 | return url.String(), nil 33 | } 34 | -------------------------------------------------------------------------------- /server/cmd/blob/pkg/mysql/mysql.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 5 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 6 | "github.com/bwmarrin/snowflake" 7 | "github.com/cloudwego/kitex/pkg/klog" 8 | "gorm.io/gorm" 9 | ) 10 | 11 | type BlobRecord struct { 12 | ID string `gorm:"primarykey"` 13 | AccountId string `gorm:"column:account_id"` 14 | Path string `gorm:"column:path;type:varchar(100);not null"` 15 | } 16 | 17 | // BeforeCreate uses snowflake to generate an BlobID and path. 18 | func (b *BlobRecord) BeforeCreate(_ *gorm.DB) (err error) { 19 | sf, err := snowflake.NewNode(consts.BlobSnowflakeNode) 20 | if err != nil { 21 | klog.Fatalf("generate id failed: %s", err.Error()) 22 | } 23 | b.ID = sf.Generate().String() 24 | return nil 25 | } 26 | 27 | type Manager struct { 28 | db *gorm.DB 29 | } 30 | 31 | // NewManager creates a mysql manager. 32 | func NewManager(db *gorm.DB) *Manager { 33 | m := db.Migrator() 34 | if !m.HasTable(&BlobRecord{}) { 35 | if err := m.CreateTable(&BlobRecord{}); err != nil { 36 | panic(err) 37 | } 38 | } 39 | return &Manager{db: db} 40 | } 41 | 42 | func (m *Manager) CreateBlobRecord(br *BlobRecord) error { 43 | return m.db.Model(&BlobRecord{}).Create(br).Error 44 | } 45 | 46 | func (m *Manager) DeleteBlobRecord(bid string) error { 47 | err := m.db.Model(&BlobRecord{}). 48 | Where(&BlobRecord{ID: bid}).First(&BlobRecord{}).Error 49 | if err != nil { 50 | if err == gorm.ErrRecordNotFound { 51 | return errno.RecordNotFound 52 | } else { 53 | return err 54 | } 55 | } 56 | return m.db.Model(&BlobRecord{}).Delete(&BlobRecord{ID: bid}).Error 57 | } 58 | 59 | func (m *Manager) GetBlobRecord(bid string) (*BlobRecord, error) { 60 | var br BlobRecord 61 | err := m.db.Model(&BlobRecord{}). 62 | Where(&BlobRecord{ID: bid}).First(&br).Error 63 | if err != nil { 64 | if err == gorm.ErrRecordNotFound { 65 | return nil, errno.RecordNotFound 66 | } else { 67 | return nil, err 68 | } 69 | } 70 | return &br, err 71 | } 72 | -------------------------------------------------------------------------------- /server/cmd/car/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | kitex -service car -module github.com/CyanAsterisk/FreeCar -use github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen ./../../idl/rpc/car.thrift 3 | rm build.sh 4 | rm -rf script -------------------------------------------------------------------------------- /server/cmd/car/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/car_srv' -------------------------------------------------------------------------------- /server/cmd/car/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type MongoDBConfig struct { 4 | Host string `mapstructure:"host" json:"host"` 5 | Port int `mapstructure:"port" json:"port"` 6 | Name string `mapstructure:"db" json:"db"` 7 | User string `mapstructure:"user" json:"user"` 8 | Password string `mapstructure:"password" json:"password"` 9 | Collection string `mapstructure:"collection" json:"collection"` 10 | } 11 | 12 | type RabbitMqConfig struct { 13 | Host string `mapstructure:"host" json:"host"` 14 | Port int `mapstructure:"port" json:"port"` 15 | Exchange string `mapstructure:"exchange" json:"exchange"` 16 | User string `mapstructure:"user" json:"user"` 17 | Password string `mapstructure:"password" json:"password"` 18 | } 19 | 20 | type ConsulConfig struct { 21 | Host string `mapstructure:"host" json:"host"` 22 | Port int `mapstructure:"port" json:"port"` 23 | Key string `mapstructure:"key" json:"key"` 24 | } 25 | 26 | type OtelConfig struct { 27 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 28 | } 29 | 30 | type ServerConfig struct { 31 | Name string `mapstructure:"name" json:"name"` 32 | Host string `mapstructure:"host" json:"host"` 33 | WsAddr string `mapstructure:"wsAddr" json:"wsAddr"` 34 | MongoDBInfo MongoDBConfig `mapstructure:"mongodb" json:"mongodb"` 35 | RabbitMqInfo RabbitMqConfig `mapstructure:"rabbitmq" json:"rabbitmq"` 36 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 37 | TripSrvInfo TripSrvConfig `mapstructure:"trip_srv" json:"trip_srv"` 38 | } 39 | 40 | type TripSrvConfig struct { 41 | Name string `mapstructure:"name" json:"name"` 42 | } 43 | -------------------------------------------------------------------------------- /server/cmd/car/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | var ( 4 | GlobalServerConfig ServerConfig 5 | GlobalConsulConfig ConsulConfig 6 | ) 7 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/car_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | "github.com/cloudwego/kitex/client" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/loadbalance" 12 | "github.com/cloudwego/kitex/pkg/rpcinfo" 13 | "github.com/kitex-contrib/obs-opentelemetry/provider" 14 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 15 | consul "github.com/kitex-contrib/registry-consul" 16 | ) 17 | 18 | // InitCar to init car service 19 | func InitCar() *carservice.Client { 20 | // init resolver 21 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 22 | config.GlobalConsulConfig.Host, 23 | config.GlobalConsulConfig.Port)) 24 | if err != nil { 25 | hlog.Fatalf("new consul client failed: %s", err.Error()) 26 | } 27 | // init OpenTelemetry 28 | provider.NewOpenTelemetryProvider( 29 | provider.WithServiceName(config.GlobalServerConfig.Name), 30 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 31 | provider.WithInsecure(), 32 | ) 33 | 34 | // create a new client 35 | c, err := carservice.NewClient( 36 | config.GlobalServerConfig.Name, 37 | client.WithResolver(r), // service discovery 38 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 39 | client.WithMuxConnection(1), // multiplexing 40 | client.WithSuite(tracing.NewClientSuite()), 41 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.Name}), 42 | ) 43 | if err != nil { 44 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 45 | } 46 | return &c 47 | } 48 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.CarConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/db.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | "go.mongodb.org/mongo-driver/mongo/options" 12 | ) 13 | 14 | // InitDB to init database 15 | func InitDB() *mongo.Database { 16 | c := config.GlobalServerConfig.MongoDBInfo 17 | mongoClient, err := mongo.Connect(context.Background(), options.Client().ApplyURI( 18 | fmt.Sprintf(consts.MongoURI, c.User, c.Password, c.Host, c.Port))) 19 | if err != nil { 20 | klog.Fatal("cannot connect mongodb", err) 21 | } 22 | return mongoClient.Database(c.Name) 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/flag.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | // InitFlag to init flag 12 | func InitFlag() (string, int) { 13 | IP := flag.String(consts.IPFlagName, consts.IPFlagValue, consts.IPFlagUsage) 14 | Port := flag.Int(consts.PortFlagName, 0, consts.PortFlagUsage) 15 | // Parsing flags and if Port is 0 , then will automatically get an empty Port. 16 | flag.Parse() 17 | if *Port == 0 { 18 | *Port, _ = tools.GetFreePort() 19 | } 20 | klog.Info("ip: ", *IP) 21 | klog.Info("port: ", *Port) 22 | return *IP, *Port 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "time" 7 | 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus" 11 | "gopkg.in/natefinch/lumberjack.v2" 12 | ) 13 | 14 | // InitLogger to init logrus 15 | func InitLogger() { 16 | // Customizable output directory. 17 | logFilePath := consts.KlogFilePath 18 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 19 | panic(err) 20 | } 21 | 22 | // Set filename to date 23 | logFileName := time.Now().Format("2006-01-02") + ".log" 24 | fileName := path.Join(logFilePath, logFileName) 25 | if _, err := os.Stat(fileName); err != nil { 26 | if _, err := os.Create(fileName); err != nil { 27 | panic(err) 28 | } 29 | } 30 | 31 | logger := kitexlogrus.NewLogger() 32 | // Provides compression and deletion 33 | lumberjackLogger := &lumberjack.Logger{ 34 | Filename: fileName, 35 | MaxSize: 20, // A file can be up to 20M. 36 | MaxBackups: 5, // Save up to 5 files at the same time. 37 | MaxAge: 10, // A file can exist for a maximum of 10 days. 38 | Compress: true, // Compress with gzip. 39 | } 40 | 41 | logger.SetOutput(lumberjackLogger) 42 | logger.SetLevel(klog.LevelWarn) 43 | 44 | klog.SetLogger(logger) 45 | } 46 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/mq.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | "github.com/streadway/amqp" 10 | ) 11 | 12 | // InitMq to init rabbitMQ 13 | func InitMq() *amqp.Connection { 14 | c := config.GlobalServerConfig.RabbitMqInfo 15 | amqpConn, err := amqp.Dial(fmt.Sprintf(consts.RabbitMqURI, c.User, c.Password, c.Host, c.Port)) 16 | if err != nil { 17 | klog.Fatal("cannot dial amqp", err) 18 | } 19 | return amqpConn 20 | } 21 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/registry" 12 | "github.com/cloudwego/kitex/pkg/utils" 13 | "github.com/hashicorp/consul/api" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry(Port int) (registry.Registry, *registry.Info) { 19 | r, err := consul.NewConsulRegister(net.JoinHostPort( 20 | config.GlobalConsulConfig.Host, 21 | strconv.Itoa(config.GlobalConsulConfig.Port)), 22 | consul.WithCheck(&api.AgentServiceCheck{ 23 | Interval: consts.ConsulCheckInterval, 24 | Timeout: consts.ConsulCheckTimeout, 25 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 26 | })) 27 | if err != nil { 28 | klog.Fatalf("new consul register failed: %s", err.Error()) 29 | } 30 | 31 | // Using snowflake to generate service name. 32 | sf, err := snowflake.NewNode(2) 33 | if err != nil { 34 | klog.Fatalf("generate service name failed: %s", err.Error()) 35 | } 36 | info := ®istry.Info{ 37 | ServiceName: config.GlobalServerConfig.Name, 38 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, strconv.Itoa(Port))), 39 | Tags: map[string]string{ 40 | "ID": sf.Generate().Base36(), 41 | }, 42 | } 43 | return r, info 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/car/initialize/trip_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip/tripservice" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | "github.com/cloudwego/kitex/client" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/loadbalance" 12 | "github.com/cloudwego/kitex/pkg/rpcinfo" 13 | "github.com/kitex-contrib/obs-opentelemetry/provider" 14 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 15 | consul "github.com/kitex-contrib/registry-consul" 16 | ) 17 | 18 | // InitTrip to init trip service 19 | func InitTrip() *tripservice.Client { 20 | // init resolver 21 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 22 | config.GlobalConsulConfig.Host, 23 | config.GlobalConsulConfig.Port)) 24 | if err != nil { 25 | hlog.Fatalf("new consul client failed: %s", err.Error()) 26 | } 27 | // init OpenTelemetry 28 | provider.NewOpenTelemetryProvider( 29 | provider.WithServiceName(config.GlobalServerConfig.TripSrvInfo.Name), 30 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 31 | provider.WithInsecure(), 32 | ) 33 | 34 | // create a new client 35 | c, err := tripservice.NewClient( 36 | config.GlobalServerConfig.TripSrvInfo.Name, 37 | client.WithResolver(r), // service discovery 38 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 39 | client.WithMuxConnection(1), // multiplexing 40 | client.WithSuite(tracing.NewClientSuite()), 41 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.TripSrvInfo.Name}), 42 | ) 43 | if err != nil { 44 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 45 | } 46 | return &c 47 | } 48 | -------------------------------------------------------------------------------- /server/cmd/car/kitex.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'car' 3 | ToolVersion: 'v0.4.4' 4 | -------------------------------------------------------------------------------- /server/cmd/car/pkg/mq/mq.go: -------------------------------------------------------------------------------- 1 | package mq 2 | 3 | import ( 4 | "context" 5 | 6 | car "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 7 | ) 8 | 9 | // Publisher defines the publishing interface. 10 | type Publisher interface { 11 | Publish(context.Context, *car.CarEntity) error 12 | } 13 | 14 | // Subscriber defines a car update subscriber. 15 | type Subscriber interface { 16 | Subscribe(context.Context) (ch chan *car.CarEntity, cleanUp func(), err error) 17 | } 18 | -------------------------------------------------------------------------------- /server/cmd/car/pkg/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/id" 9 | car "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 10 | "github.com/bytedance/sonic" 11 | "github.com/go-redis/redis/v8" 12 | ) 13 | 14 | type Manager struct { 15 | client *redis.Client 16 | } 17 | 18 | // NewManager creates a redis manager. 19 | func NewManager(client *redis.Client) *Manager { 20 | return &Manager{client: client} 21 | } 22 | 23 | func (m *Manager) GetCar(c context.Context, cid id.CarID) (*car.CarEntity, error) { 24 | cj, err := m.client.Get(c, cid.String()).Result() 25 | if err != nil { 26 | if err == redis.Nil { 27 | return nil, errno.RecordNotFound 28 | } 29 | return nil, err 30 | } 31 | var cr car.Car 32 | if err = sonic.UnmarshalString(cj, &cr); err != nil { 33 | return nil, err 34 | } 35 | return &car.CarEntity{ 36 | Id: cid.String(), 37 | Car: &cr, 38 | }, nil 39 | } 40 | 41 | func (m *Manager) InsertCar(c context.Context, cid id.CarID, cr *car.Car) error { 42 | cj, err := sonic.Marshal(cr) 43 | if err != nil { 44 | return err 45 | } 46 | if err = m.client.Set(c, cid.String(), cj, 168*time.Hour).Err(); err != nil { 47 | return err 48 | } 49 | return nil 50 | } 51 | 52 | func (m *Manager) RemoveCar(c context.Context, cid id.CarID) error { 53 | return m.client.Del(c, cid.String()).Err() 54 | } 55 | -------------------------------------------------------------------------------- /server/cmd/car/pkg/trip/updater.go: -------------------------------------------------------------------------------- 1 | package trip 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/pkg/mq" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip/tripservice" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | ) 12 | 13 | // RunUpdater runs a trip updater. 14 | func RunUpdater(sub mq.Subscriber, ts tripservice.Client) { 15 | ch, cleanUp, err := sub.Subscribe(context.Background()) 16 | defer cleanUp() 17 | 18 | if err != nil { 19 | klog.Fatal("cannot subscribe: %s", err.Error()) 20 | } 21 | 22 | for car := range ch { 23 | if car.Car.Status == base.CarStatus_UNLOCKED && 24 | car.Car.TripId != "" && car.Car.Driver.Id != "" { 25 | _, err := ts.UpdateTrip(context.Background(), &trip.UpdateTripRequest{ 26 | Id: car.Car.TripId, 27 | Current: &base.Location{ 28 | Latitude: car.Car.Position.Latitude, 29 | Longitude: car.Car.Position.Longitude, 30 | }, 31 | AccountId: car.Car.Driver.Id, 32 | }) 33 | if err != nil { 34 | klog.Errorf("cannot update trip : tripId = %s err: %s", car.Car.TripId, err.Error()) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /server/cmd/car/pkg/ws/ws.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/car/pkg/mq" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/hertz-contrib/websocket" 11 | ) 12 | 13 | var upgrader = websocket.HertzUpgrader{} 14 | 15 | // Handler creates a websocket http handler. 16 | func Handler(sub mq.Subscriber) app.HandlerFunc { 17 | return func(c context.Context, ctx *app.RequestContext) { 18 | err := upgrader.Upgrade(ctx, func(conn *websocket.Conn) { 19 | msgs, cleanUp, err := sub.Subscribe(context.Background()) 20 | defer cleanUp() 21 | if err != nil { 22 | klog.Error("cannot subscribe", err) 23 | ctx.String(http.StatusInternalServerError, "") 24 | return 25 | } 26 | 27 | done := make(chan struct{}) 28 | go func() { 29 | for { 30 | _, _, err := conn.ReadMessage() 31 | if err != nil { 32 | if !websocket.IsCloseError(err, 33 | websocket.CloseGoingAway, 34 | websocket.CloseNormalClosure) { 35 | klog.Warn("unexpected read error", err) 36 | } 37 | done <- struct{}{} 38 | break 39 | } 40 | } 41 | }() 42 | 43 | for { 44 | select { 45 | case msg := <-msgs: 46 | err := conn.WriteJSON(msg) 47 | if err != nil { 48 | klog.Warn("cannot write JSON %s", err.Error()) 49 | } 50 | case <-done: 51 | return 52 | } 53 | } 54 | }) 55 | if err != nil { 56 | klog.Warnf("Upgrade err: %s", err.Error()) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /server/cmd/profile/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | kitex -service profile -module github.com/CyanAsterisk/FreeCar -use github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen ./../../idl/rpc/profile.thrift 3 | rm build.sh 4 | rm -rf script -------------------------------------------------------------------------------- /server/cmd/profile/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/profile_srv' -------------------------------------------------------------------------------- /server/cmd/profile/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type MongoDBConfig struct { 4 | Host string `mapstructure:"host" json:"host"` 5 | Port int `mapstructure:"port" json:"port"` 6 | Name string `mapstructure:"db" json:"db"` 7 | User string `mapstructure:"user" json:"user"` 8 | Password string `mapstructure:"password" json:"password"` 9 | Collection string `mapstructure:"collection" json:"collection"` 10 | } 11 | 12 | type ConsulConfig struct { 13 | Host string `mapstructure:"host" json:"host"` 14 | Port int `mapstructure:"port" json:"port"` 15 | Key string `mapstructure:"key" json:"key"` 16 | } 17 | 18 | type OtelConfig struct { 19 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 20 | } 21 | 22 | type OCRConfig struct { 23 | AccessToken string `mapstructure:"access_token" json:"access_token"` 24 | MockEnable bool `mapstructure:"mock_enable" json:"mock_enable"` 25 | MockUrl string `mapstructure:"mock_url" json:"mock_url"` 26 | } 27 | 28 | type ServerConfig struct { 29 | Name string `mapstructure:"name" json:"name"` 30 | Host string `mapstructure:"host" json:"host"` 31 | MongoDBInfo MongoDBConfig `mapstructure:"mongodb" json:"mongodb"` 32 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 33 | OCRConfig OCRConfig `mapstructure:"ocr" json:"ocr"` 34 | BlobSrvInfo BlobSrvConfig `mapstructure:"blob_srv" json:"blob_srv"` 35 | } 36 | 37 | type BlobSrvConfig struct { 38 | Name string `mapstructure:"name" json:"name"` 39 | } 40 | -------------------------------------------------------------------------------- /server/cmd/profile/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | var ( 4 | GlobalServerConfig ServerConfig 5 | GlobalConsulConfig ConsulConfig 6 | ) 7 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/blob_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob/blobservice" 8 | "github.com/cloudwego/kitex/client" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "github.com/cloudwego/kitex/pkg/loadbalance" 11 | "github.com/cloudwego/kitex/pkg/rpcinfo" 12 | "github.com/kitex-contrib/obs-opentelemetry/provider" 13 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitBlob to init blob service 18 | func InitBlob() blobservice.Client { 19 | // init resolver 20 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 21 | config.GlobalConsulConfig.Host, 22 | config.GlobalConsulConfig.Port)) 23 | if err != nil { 24 | klog.Fatalf("new consul client failed: %s", err.Error()) 25 | } 26 | provider.NewOpenTelemetryProvider( 27 | provider.WithServiceName(config.GlobalServerConfig.BlobSrvInfo.Name), 28 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 29 | provider.WithInsecure(), 30 | ) 31 | 32 | // create a new client 33 | c, err := blobservice.NewClient( 34 | config.GlobalServerConfig.BlobSrvInfo.Name, 35 | client.WithResolver(r), // service discovery 36 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 37 | client.WithMuxConnection(1), // multiplexing 38 | client.WithSuite(tracing.NewClientSuite()), 39 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.BlobSrvInfo.Name}), 40 | ) 41 | 42 | if err != nil { 43 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 44 | } 45 | return c 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.ProfileConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/db.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | "go.mongodb.org/mongo-driver/mongo/options" 12 | ) 13 | 14 | // InitDB to init database 15 | func InitDB() *mongo.Database { 16 | c := config.GlobalServerConfig.MongoDBInfo 17 | mongoClient, err := mongo.Connect(context.Background(), options.Client().ApplyURI( 18 | fmt.Sprintf(consts.MongoURI, c.User, c.Password, c.Host, c.Port))) 19 | if err != nil { 20 | klog.Fatal("cannot connect mongodb", err) 21 | } 22 | return mongoClient.Database(c.Name) 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/flag.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | // InitFlag to init flag 12 | func InitFlag() (string, int) { 13 | IP := flag.String(consts.IPFlagName, consts.IPFlagValue, consts.IPFlagUsage) 14 | Port := flag.Int(consts.PortFlagName, 0, consts.PortFlagUsage) 15 | // Parsing flags and if Port is 0 , then will automatically get an empty Port. 16 | flag.Parse() 17 | if *Port == 0 { 18 | *Port, _ = tools.GetFreePort() 19 | } 20 | klog.Info("ip: ", *IP) 21 | klog.Info("port: ", *Port) 22 | return *IP, *Port 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus" 12 | "gopkg.in/natefinch/lumberjack.v2" 13 | ) 14 | 15 | // InitLogger to init logrus 16 | func InitLogger() { 17 | // Customizable output directory. 18 | logFilePath := consts.KlogFilePath 19 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 20 | panic(err) 21 | } 22 | 23 | // Set filename to date 24 | logFileName := time.Now().Format("2006-01-02") + ".log" 25 | fileName := path.Join(logFilePath, logFileName) 26 | if _, err := os.Stat(fileName); err != nil { 27 | if _, err := os.Create(fileName); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | logger := kitexlogrus.NewLogger() 33 | // Provides compression and deletion 34 | lumberjackLogger := &lumberjack.Logger{ 35 | Filename: fileName, 36 | MaxSize: 20, // A file can be up to 20M. 37 | MaxBackups: 5, // Save up to 5 files at the same time. 38 | MaxAge: 10, // A file can exist for a maximum of 10 days. 39 | Compress: true, // Compress with gzip. 40 | } 41 | 42 | if runtime.GOOS == "linux" { 43 | logger.SetOutput(lumberjackLogger) 44 | logger.SetLevel(klog.LevelWarn) 45 | } else { 46 | logger.SetLevel(klog.LevelDebug) 47 | } 48 | 49 | klog.SetLogger(logger) 50 | } 51 | -------------------------------------------------------------------------------- /server/cmd/profile/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/registry" 12 | "github.com/cloudwego/kitex/pkg/utils" 13 | "github.com/hashicorp/consul/api" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry(Port int) (registry.Registry, *registry.Info) { 19 | r, err := consul.NewConsulRegister(net.JoinHostPort( 20 | config.GlobalConsulConfig.Host, 21 | strconv.Itoa(config.GlobalConsulConfig.Port)), 22 | consul.WithCheck(&api.AgentServiceCheck{ 23 | Interval: consts.ConsulCheckInterval, 24 | Timeout: consts.ConsulCheckTimeout, 25 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 26 | })) 27 | if err != nil { 28 | klog.Fatalf("new consul register failed: %s", err.Error()) 29 | } 30 | 31 | // Using snowflake to generate service name. 32 | sf, err := snowflake.NewNode(2) 33 | if err != nil { 34 | klog.Fatalf("generate service name failed: %s", err.Error()) 35 | } 36 | info := ®istry.Info{ 37 | ServiceName: config.GlobalServerConfig.Name, 38 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, strconv.Itoa(Port))), 39 | Tags: map[string]string{ 40 | "ID": sf.Generate().Base36(), 41 | }, 42 | } 43 | return r, info 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/profile/kitex.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'profile' 3 | ToolVersion: 'v0.4.4' 4 | -------------------------------------------------------------------------------- /server/cmd/profile/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "strconv" 7 | 8 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/config" 9 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/initialize" 10 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/pkg/mongo" 11 | "github.com/CyanAsterisk/FreeCar/server/cmd/profile/pkg/ocr" 12 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 13 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 14 | "github.com/cloudwego/kitex/pkg/klog" 15 | "github.com/cloudwego/kitex/pkg/limit" 16 | "github.com/cloudwego/kitex/pkg/rpcinfo" 17 | "github.com/cloudwego/kitex/pkg/utils" 18 | "github.com/cloudwego/kitex/server" 19 | "github.com/kitex-contrib/obs-opentelemetry/provider" 20 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 21 | ) 22 | 23 | func main() { 24 | // initialization 25 | initialize.InitLogger() 26 | initialize.InitConfig() 27 | IP, Port := initialize.InitFlag() 28 | r, info := initialize.InitRegistry(Port) 29 | mongoDb := initialize.InitDB() 30 | blobClient := initialize.InitBlob() 31 | p := provider.NewOpenTelemetryProvider( 32 | provider.WithServiceName(config.GlobalServerConfig.Name), 33 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 34 | provider.WithInsecure(), 35 | ) 36 | defer p.Shutdown(context.Background()) 37 | 38 | // Create new server. 39 | srv := profileservice.NewServer(&ProfileServiceImpl{ 40 | MongoManager: mongo.NewManager(mongoDb), 41 | BlobManager: blobClient, 42 | LicenseManager: &ocr.LicenseManager{}, 43 | }, 44 | server.WithServiceAddr(utils.NewNetAddr(consts.TCP, net.JoinHostPort(IP, strconv.Itoa(Port)))), 45 | server.WithRegistry(r), 46 | server.WithRegistryInfo(info), 47 | server.WithLimit(&limit.Option{MaxConnections: 2000, MaxQPS: 500}), 48 | server.WithSuite(tracing.NewServerSuite()), 49 | server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.Name}), 50 | ) 51 | 52 | err := srv.Run() 53 | if err != nil { 54 | klog.Fatal(err) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /server/cmd/profile/pkg/redis/redis.go: -------------------------------------------------------------------------------- 1 | package redis 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/id" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 10 | "github.com/bytedance/sonic" 11 | "github.com/go-redis/redis/v8" 12 | ) 13 | 14 | type Manager struct { 15 | RedisClient *redis.Client 16 | } 17 | 18 | func NewManager(client *redis.Client) *Manager { 19 | return &Manager{RedisClient: client} 20 | } 21 | 22 | func (r *Manager) GetProfile(c context.Context, aid id.AccountID) (*base.Profile, error) { 23 | p, err := r.RedisClient.Get(c, aid.String()).Result() 24 | if err != nil { 25 | if err == redis.Nil { 26 | return nil, errno.RecordNotFound 27 | } 28 | return nil, err 29 | } 30 | var pv base.Profile 31 | if err = sonic.UnmarshalString(p, &pv); err != nil { 32 | return nil, err 33 | } 34 | return &pv, nil 35 | } 36 | 37 | func (r *Manager) InsertProfile(c context.Context, aid id.AccountID, p *base.Profile) error { 38 | _, err := r.RedisClient.Get(c, aid.String()).Result() 39 | if err != redis.Nil { 40 | if err == nil { 41 | return errno.RecordAlreadyExist 42 | } else { 43 | return err 44 | } 45 | } 46 | pv, err := sonic.Marshal(p) 47 | if err != nil { 48 | return err 49 | } 50 | if err = r.RedisClient.Set(c, aid.String(), pv, 168*time.Hour).Err(); err != nil { 51 | return err 52 | } 53 | return nil 54 | } 55 | 56 | func (r *Manager) RemoveProfile(c context.Context, aid id.AccountID) error { 57 | if err := r.RedisClient.Del(c, aid.String()).Err(); err != nil { 58 | return err 59 | } 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /server/cmd/trade/.gitattributes: -------------------------------------------------------------------------------- 1 | /mvnw text eol=lf 2 | *.cmd text eol=crlf 3 | -------------------------------------------------------------------------------- /server/cmd/trade/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /server/cmd/trade/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | wrapperVersion=3.3.2 18 | distributionType=only-script 19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip 20 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/FreecarTradeApplication.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.cloud.client.discovery.EnableDiscoveryClient; 6 | import org.springframework.cloud.context.config.annotation.RefreshScope; 7 | import org.springframework.scheduling.annotation.EnableScheduling; 8 | 9 | @RefreshScope 10 | @SpringBootApplication 11 | @EnableDiscoveryClient 12 | @EnableScheduling 13 | public class FreecarTradeApplication { 14 | 15 | public static void main(String[] args) { 16 | SpringApplication.run(FreecarTradeApplication.class, args); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/annotation/MonitorLog.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.annotation; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Target(ElementType.METHOD) 9 | @Retention(RetentionPolicy.RUNTIME) 10 | public @interface MonitorLog { 11 | String description() default ""; 12 | } 13 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/annotation/aspect/MonitorLogAspect.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.annotation.aspect; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import org.aspectj.lang.ProceedingJoinPoint; 5 | import org.aspectj.lang.annotation.Around; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.aspectj.lang.annotation.Pointcut; 9 | import org.aspectj.lang.reflect.MethodSignature; 10 | import org.lanlance.freecartrade.annotation.MonitorLog; 11 | import org.springframework.stereotype.Component; 12 | 13 | @Aspect 14 | @Component 15 | @Slf4j 16 | public class MonitorLogAspect { 17 | 18 | private final ObjectMapper objectMapper; 19 | 20 | public MonitorLogAspect(ObjectMapper objectMapper) { 21 | this.objectMapper = objectMapper; 22 | } 23 | 24 | @Pointcut("@annotation(org.lanlance.freecartrade.annotation.MonitorLog)") 25 | public void monitorLogPointcut() { 26 | } 27 | 28 | @Around("monitorLogPointcut()") 29 | public Object around(ProceedingJoinPoint point) throws Throwable { 30 | long startTime = System.currentTimeMillis(); 31 | String methodName = point.getSignature().getName(); 32 | MonitorLog monitorLog = ((MethodSignature) point.getSignature()).getMethod().getAnnotation(MonitorLog.class); 33 | 34 | try { 35 | String args = objectMapper.writeValueAsString(point.getArgs()); 36 | log.info("Method start: {}, des: {}, param: {}", methodName, monitorLog.description(), args); 37 | Object result = point.proceed(); 38 | long endTime = System.currentTimeMillis(); 39 | log.info("Method end: {}, time-consume: {}ms", methodName, (endTime - startTime)); 40 | return result; 41 | } catch (Exception e) { 42 | log.error("Method error: {}, msg: {}", methodName, e.getMessage()); 43 | throw e; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/config/CryptoConfig.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.config; 2 | 3 | import org.bouncycastle.jce.provider.BouncyCastleProvider; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import java.security.Security; 7 | 8 | @Configuration 9 | public class CryptoConfig { 10 | 11 | static { 12 | if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 13 | Security.addProvider(new BouncyCastleProvider()); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/config/PasetoConfig.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.config; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | import org.springframework.context.annotation.Configuration; 6 | 7 | @Data 8 | @Configuration 9 | @ConfigurationProperties(prefix = "paseto") 10 | public class PasetoConfig { 11 | private String pubKey; 12 | private String implicit; 13 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/config/PaymentStatusEnum.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.config; 2 | 3 | import lombok.Getter; 4 | 5 | @Getter 6 | public enum PaymentStatusEnum { 7 | PROCESSING("Processing"), 8 | FAILED("Failed"), 9 | PAID("Paid"); 10 | 11 | private final String description; 12 | 13 | PaymentStatusEnum(String description) { 14 | this.description = description; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/config/RabbitConfig.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.config; 2 | 3 | import org.springframework.amqp.core.*; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | @Configuration 9 | public class RabbitConfig { 10 | 11 | @Value("${spring.rabbitmq.exchange-name}") 12 | private String exchangeName; 13 | 14 | @Value("${spring.rabbitmq.queue-name}") 15 | private String queueName; 16 | 17 | @Value("${spring.rabbitmq.dlq.exchange-name}") 18 | private String dlxExchangeName; 19 | 20 | @Value("${spring.rabbitmq.dlq.queue-name}") 21 | private String dlqQueueName; 22 | 23 | @Bean 24 | public Queue tradeQueue() { 25 | return QueueBuilder.durable(queueName) 26 | .withArgument("x-dead-letter-exchange", dlxExchangeName) 27 | .withArgument("x-dead-letter-routing-key", dlqQueueName) 28 | .build(); 29 | } 30 | 31 | @Bean 32 | public FanoutExchange paymentExchange() { 33 | return new FanoutExchange(exchangeName); 34 | } 35 | 36 | @Bean 37 | public Binding binding() { 38 | return BindingBuilder.bind(tradeQueue()).to(paymentExchange()); 39 | } 40 | 41 | @Bean 42 | public Queue dlqQueue() { 43 | return new Queue(dlqQueueName, true); 44 | } 45 | 46 | @Bean 47 | public DirectExchange dlxExchange() { 48 | return new DirectExchange(dlxExchangeName); 49 | } 50 | 51 | @Bean 52 | public Binding dlqBinding() { 53 | return BindingBuilder.bind(dlqQueue()).to(dlxExchange()).with(dlqQueueName); 54 | } 55 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.config; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.lanlance.freecartrade.interceptor.PasetoAuthInterceptor; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 9 | 10 | @Configuration 11 | @RequiredArgsConstructor 12 | public class WebMvcConfig implements WebMvcConfigurer { 13 | 14 | @Autowired 15 | private PasetoAuthInterceptor pasetoAuthInterceptor; 16 | 17 | @Override 18 | public void addInterceptors(InterceptorRegistry registry) { 19 | // Auth Interceptor 20 | registry.addInterceptor(pasetoAuthInterceptor).addPathPatterns("/**").excludePathPatterns("/actuator/**"); 21 | } 22 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/controller/TradeController.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.controller; 2 | 3 | import jakarta.servlet.http.HttpServletRequest; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.lanlance.freecartrade.interceptor.PasetoAuthInterceptor; 7 | import org.lanlance.freecartrade.model.resp.Result; 8 | import org.lanlance.freecartrade.model.req.RechargeRequest; 9 | import org.lanlance.freecartrade.service.RechargeServiceIface; 10 | import org.lanlance.freecartrade.util.ResultUtil; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.math.BigDecimal; 15 | 16 | @RestController 17 | @RequestMapping("/trade") 18 | @Slf4j 19 | @RequiredArgsConstructor 20 | public class TradeController { 21 | 22 | @Autowired 23 | private RechargeServiceIface rechargeService; 24 | 25 | @PostMapping("/recharge") 26 | public Result recharge(HttpServletRequest httpReq, @RequestBody RechargeRequest req) { 27 | Integer amount = req.getAmount(); 28 | if (amount == null || amount <= 0) { 29 | log.error("recharge# Invalid amount: {}", amount); 30 | return ResultUtil.error("Invalid amount"); 31 | } 32 | String userId = (String) httpReq.getAttribute(PasetoAuthInterceptor.ACCOUNT_ID); 33 | if (userId == null || userId.isEmpty()) { 34 | log.error("recharge# UserID is null or empty"); 35 | return ResultUtil.error(ResultUtil.UNAUTHORIZED, "unauthorized"); 36 | } 37 | 38 | try { 39 | rechargeService.recharge(userId, amount); 40 | return ResultUtil.success(); 41 | } catch (Exception e) { 42 | log.error("recharge# Recharge failed: ", e); 43 | return ResultUtil.error("Recharge failed"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/model/PayInfo.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | 8 | @Data 9 | @AllArgsConstructor 10 | public class PayInfo { 11 | @JsonProperty("account_id") 12 | private String AccountID; 13 | 14 | @JsonProperty("trip_id") 15 | private String TripID; 16 | 17 | @JsonProperty("fee_cent") 18 | private Integer FeeCent; 19 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/model/RedisDeductResult.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @Data 7 | @AllArgsConstructor 8 | public class RedisDeductResult { 9 | private boolean success; 10 | private long newBalance; 11 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/model/req/RechargeRequest.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.model.req; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class RechargeRequest { 11 | private Integer amount; 12 | } 13 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/model/resp/Result.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.model.resp; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class Result { 7 | private Integer code; 8 | private String msg; 9 | private T data; 10 | 11 | public Result() {} 12 | 13 | public Result(Integer code, String msg, T data) { 14 | this.code = code; 15 | this.msg = msg; 16 | this.data = data; 17 | } 18 | 19 | public Result(Integer code, String msg) { 20 | this.code = code; 21 | this.msg = msg; 22 | } 23 | 24 | public Result(Integer code) { 25 | this.code = code; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/mq/TradeMessageListener.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.mq; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.lanlance.freecartrade.service.TradeServiceIface; 5 | import org.springframework.amqp.core.Message; 6 | import org.springframework.amqp.rabbit.annotation.RabbitListener; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.Map; 11 | 12 | @Component 13 | @Slf4j 14 | public class TradeMessageListener { 15 | 16 | @Autowired 17 | private TradeServiceIface tradeService; 18 | 19 | @RabbitListener(queues = "${spring.rabbitmq.queue-name}") 20 | public void receiveMessage(Message message) { 21 | String content = new String(message.getBody()); 22 | tradeService.processTradeMessage(content); 23 | } 24 | 25 | @RabbitListener(queues = "${spring.rabbitmq.dlq.queue-name}") 26 | public void handleDeadLetterMessage(Message message) { 27 | String content = new String(message.getBody()); 28 | Map headers = message.getMessageProperties().getHeaders(); 29 | log.error("[DLQ] Dead letter from queue: {}, reason: {}, content: {}", 30 | headers.get("x-first-death-queue"), 31 | headers.get("x-first-death-reason"), 32 | content 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/repository/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.repository.mapper; 2 | 3 | import org.apache.ibatis.annotations.Mapper; 4 | import org.apache.ibatis.annotations.Param; 5 | import org.apache.ibatis.annotations.Select; 6 | import org.apache.ibatis.annotations.Update; 7 | 8 | @Mapper 9 | public interface UserMapper { 10 | 11 | @Select("SELECT EXISTS(SELECT 1 FROM user WHERE id = #{userId})") 12 | boolean existsById(@Param("userId") String userId); 13 | 14 | @Update("UPDATE user SET balance = balance + #{amount} WHERE id = #{userId}") 15 | int updateBalance(@Param("userId") String userId, @Param("amount") Integer amount); 16 | } 17 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/repository/mongo/TripRepository.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.repository.mongo; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import org.bson.Document; 5 | import org.lanlance.freecartrade.config.PaymentStatusEnum; 6 | import org.springframework.data.mongodb.core.MongoTemplate; 7 | import org.springframework.data.mongodb.core.query.Criteria; 8 | import org.springframework.data.mongodb.core.query.Query; 9 | import org.springframework.data.mongodb.core.query.Update; 10 | import org.springframework.stereotype.Repository; 11 | 12 | import java.util.List; 13 | 14 | @Repository 15 | @RequiredArgsConstructor 16 | public class TripRepository { 17 | private final MongoTemplate mongoTemplate; 18 | 19 | public boolean updatePaymentStatus(String tripId, PaymentStatusEnum status) { 20 | Query query = Query.query(Criteria.where("_id").is(tripId)); 21 | Update update = new Update().set("trip.payment_status", status.name()); 22 | 23 | return mongoTemplate.updateFirst(query, update, "trip") 24 | .getModifiedCount() > 0; 25 | } 26 | 27 | public List findFailedPayments() { 28 | Query query = new Query(Criteria.where("trip.payment_status").is(PaymentStatusEnum.FAILED.name())); 29 | return mongoTemplate.find(query, Document.class, "trip"); 30 | } 31 | 32 | public String findTripStatusById(String tripId) { 33 | Query query = new Query(Criteria.where("_id").is(tripId)); 34 | Document trip = mongoTemplate.findOne(query, Document.class, "trip"); 35 | if (trip != null) { 36 | Document tripDoc = (Document) trip.get("trip"); 37 | return tripDoc != null ? tripDoc.getString("payment_status") : null; 38 | } 39 | return null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/service/RechargeServiceIface.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.service; 2 | 3 | public interface RechargeServiceIface { 4 | void recharge(String userId, Integer amount); 5 | } 6 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/service/TradeServiceIface.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.service; 2 | 3 | public interface TradeServiceIface { 4 | void processTradeMessage(String content); 5 | } 6 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/util/CalcUtil.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.util; 2 | 3 | public class CalcUtil { 4 | 5 | // 十六进制字符串转换为字节数组 6 | public static byte[] hexStringToByteArray(String s) { 7 | int len = s.length(); 8 | byte[] data = new byte[len / 2]; 9 | for (int i = 0; i < len; i += 2) { 10 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); 11 | } 12 | return data; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/java/org/lanlance/freecartrade/util/ResultUtil.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade.util; 2 | 3 | import org.lanlance.freecartrade.model.resp.Result; 4 | 5 | public class ResultUtil { 6 | 7 | // 成功状态码 8 | public static final Integer SUCCESS = 0; 9 | // 失败状态码 10 | public static final Integer SERVER_INTERNAL_ERROR = 1; 11 | public static final Integer UNAUTHORIZED = 401; 12 | 13 | // 成功,带数据 14 | public static Result success(T data) { 15 | return new Result<>(SUCCESS, "ok", data); 16 | } 17 | 18 | // 成功,不带数据 19 | public static Result success() { 20 | return success(null); 21 | } 22 | 23 | // 成功,自定义消息,带数据 24 | public static Result success(String msg, T data) { 25 | return new Result<>(SUCCESS, msg, data); 26 | } 27 | 28 | // 失败,带错误信息 29 | public static Result error(String msg) { 30 | return new Result<>(SERVER_INTERNAL_ERROR, msg, null); 31 | } 32 | 33 | // 失败,自定义错误码和错误信息 34 | public static Result error(Integer code, String msg) { 35 | return new Result<>(code, msg, null); 36 | } 37 | } -------------------------------------------------------------------------------- /server/cmd/trade/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.application.name=trade_srv -------------------------------------------------------------------------------- /server/cmd/trade/src/main/resources/bootstrap.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | cloud: 3 | consul: 4 | host: localhost 5 | port: 8500 6 | config: 7 | enabled: true 8 | format: YAML 9 | prefix: freecar 10 | default-context: java 11 | data-key: trade_srv 12 | watch: 13 | enabled: true 14 | discovery: 15 | port: ${server.port} 16 | service-name: ${spring.application.name} -------------------------------------------------------------------------------- /server/cmd/trade/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 6 | 7 | 8 | 9 | 10 | logs/freecar-trade.log 11 | 12 | logs/freecar-trade.%d{yyyy-MM-dd}.log 13 | 30 14 | 15 | 16 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /server/cmd/trade/src/main/resources/lua/deduct_balance.lua: -------------------------------------------------------------------------------- 1 | local balance_key = KEYS[1] 2 | local deduct_amount = tonumber(ARGV[1]) 3 | 4 | local current_balance = tonumber(redis.call('get', balance_key) or 0) 5 | 6 | if current_balance >= deduct_amount then 7 | local new_balance = current_balance - deduct_amount 8 | redis.call('set', balance_key, new_balance) 9 | return new_balance 10 | else 11 | return -1 12 | end 13 | -------------------------------------------------------------------------------- /server/cmd/trade/src/test/java/org/lanlance/freecartrade/FreecarTradeApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.lanlance.freecartrade; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class FreecarTradeApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /server/cmd/trip/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | kitex -service trip -module github.com/CyanAsterisk/FreeCar -use github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen ./../../idl/rpc/trip.thrift 3 | rm build.sh 4 | rm -rf script -------------------------------------------------------------------------------- /server/cmd/trip/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/trip_srv' -------------------------------------------------------------------------------- /server/cmd/trip/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type MongoDBConfig struct { 4 | Host string `mapstructure:"host" json:"host"` 5 | Port int `mapstructure:"port" json:"port"` 6 | Name string `mapstructure:"db" json:"db"` 7 | User string `mapstructure:"user" json:"user"` 8 | Password string `mapstructure:"password" json:"password"` 9 | Collection string `mapstructure:"collection" json:"collection"` 10 | } 11 | 12 | type ConsulConfig struct { 13 | Host string `mapstructure:"host" json:"host"` 14 | Port int `mapstructure:"port" json:"port"` 15 | Key string `mapstructure:"key" json:"key"` 16 | } 17 | 18 | type OtelConfig struct { 19 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 20 | } 21 | 22 | type RabbitMqConfig struct { 23 | Host string `mapstructure:"host" json:"host"` 24 | Port int `mapstructure:"port" json:"port"` 25 | Exchange string `mapstructure:"exchange" json:"exchange"` 26 | User string `mapstructure:"user" json:"user"` 27 | Password string `mapstructure:"password" json:"password"` 28 | } 29 | 30 | type ServerConfig struct { 31 | Name string `mapstructure:"name" json:"name"` 32 | Host string `mapstructure:"host" json:"host"` 33 | MongoDBInfo MongoDBConfig `mapstructure:"mongodb" json:"mongodb"` 34 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 35 | RabbitMqInfo RabbitMqConfig `mapstructure:"rabbitmq" json:"rabbitmq"` 36 | CarSrvInfo CarSrvConfig `mapstructure:"car_srv" json:"car_srv"` 37 | ProfileSrvInfo ProfileSrvConfig `mapstructure:"profile_srv" json:"profile_srv"` 38 | } 39 | 40 | type CarSrvConfig struct { 41 | Name string `mapstructure:"name" json:"name"` 42 | } 43 | 44 | type ProfileSrvConfig struct { 45 | Name string `mapstructure:"name" json:"name"` 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/trip/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 5 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 6 | ) 7 | 8 | var ( 9 | GlobalServerConfig ServerConfig 10 | GlobalConsulConfig ConsulConfig 11 | 12 | CarClient carservice.Client 13 | ProfileClient profileservice.Client 14 | ) 15 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/car_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | "github.com/cloudwego/kitex/client" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/loadbalance" 12 | "github.com/cloudwego/kitex/pkg/rpcinfo" 13 | "github.com/kitex-contrib/obs-opentelemetry/provider" 14 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 15 | consul "github.com/kitex-contrib/registry-consul" 16 | ) 17 | 18 | // InitCar to init car service 19 | func InitCar() { 20 | // init resolver 21 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 22 | config.GlobalConsulConfig.Host, 23 | config.GlobalConsulConfig.Port)) 24 | if err != nil { 25 | hlog.Fatalf("new consul client failed: %s", err.Error()) 26 | } 27 | // init OpenTelemetry 28 | provider.NewOpenTelemetryProvider( 29 | provider.WithServiceName(config.GlobalServerConfig.CarSrvInfo.Name), 30 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 31 | provider.WithInsecure(), 32 | ) 33 | // create a new client 34 | c, err := carservice.NewClient( 35 | config.GlobalServerConfig.CarSrvInfo.Name, 36 | client.WithResolver(r), // service discovery 37 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 38 | client.WithMuxConnection(1), // multiplexing 39 | client.WithSuite(tracing.NewClientSuite()), 40 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.CarSrvInfo.Name}), 41 | ) 42 | if err != nil { 43 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 44 | } 45 | config.CarClient = c 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.TripConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/db.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "go.mongodb.org/mongo-driver/mongo" 11 | "go.mongodb.org/mongo-driver/mongo/options" 12 | ) 13 | 14 | // InitDB to init database 15 | func InitDB() *mongo.Database { 16 | c := config.GlobalServerConfig.MongoDBInfo 17 | mongoClient, err := mongo.Connect(context.Background(), options.Client().ApplyURI( 18 | fmt.Sprintf(consts.MongoURI, c.User, c.Password, c.Host, c.Port))) 19 | if err != nil { 20 | klog.Fatal("cannot connect mongodb", err) 21 | } 22 | return mongoClient.Database(c.Name) 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/flag.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | // InitFlag to init flag 12 | func InitFlag() (string, int) { 13 | IP := flag.String(consts.IPFlagName, consts.IPFlagValue, consts.IPFlagUsage) 14 | Port := flag.Int(consts.PortFlagName, 0, consts.PortFlagUsage) 15 | // Parsing flags and if Port is 0 , then will automatically get an empty Port. 16 | flag.Parse() 17 | if *Port == 0 { 18 | *Port, _ = tools.GetFreePort() 19 | } 20 | klog.Info("ip: ", *IP) 21 | klog.Info("port: ", *Port) 22 | return *IP, *Port 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus" 12 | "gopkg.in/natefinch/lumberjack.v2" 13 | ) 14 | 15 | // InitLogger to init logrus 16 | func InitLogger() { 17 | // Customizable output directory. 18 | logFilePath := consts.KlogFilePath 19 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 20 | panic(err) 21 | } 22 | 23 | // Set filename to date 24 | logFileName := time.Now().Format("2006-01-02") + ".log" 25 | fileName := path.Join(logFilePath, logFileName) 26 | if _, err := os.Stat(fileName); err != nil { 27 | if _, err := os.Create(fileName); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | logger := kitexlogrus.NewLogger() 33 | // Provides compression and deletion 34 | lumberjackLogger := &lumberjack.Logger{ 35 | Filename: fileName, 36 | MaxSize: 20, // A file can be up to 20M. 37 | MaxBackups: 5, // Save up to 5 files at the same time. 38 | MaxAge: 10, // A file can exist for a maximum of 10 days. 39 | Compress: true, // Compress with gzip. 40 | } 41 | 42 | if runtime.GOOS == "linux" { 43 | logger.SetOutput(lumberjackLogger) 44 | logger.SetLevel(klog.LevelWarn) 45 | } else { 46 | logger.SetLevel(klog.LevelDebug) 47 | } 48 | 49 | klog.SetLogger(logger) 50 | } 51 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/mq.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | "github.com/streadway/amqp" 10 | ) 11 | 12 | // InitMq to init rabbitMQ 13 | func InitMq() *amqp.Connection { 14 | c := config.GlobalServerConfig.RabbitMqInfo 15 | amqpConn, err := amqp.Dial(fmt.Sprintf(consts.RabbitMqURI, c.User, c.Password, c.Host, c.Port)) 16 | if err != nil { 17 | klog.Fatal("cannot dial amqp", err) 18 | } 19 | return amqpConn 20 | } 21 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/profile_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | "github.com/cloudwego/kitex/client" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/loadbalance" 12 | "github.com/cloudwego/kitex/pkg/rpcinfo" 13 | "github.com/kitex-contrib/obs-opentelemetry/provider" 14 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 15 | consul "github.com/kitex-contrib/registry-consul" 16 | ) 17 | 18 | // InitProfile to init profile service 19 | func InitProfile() { 20 | // init resolver 21 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 22 | config.GlobalConsulConfig.Host, 23 | config.GlobalConsulConfig.Port)) 24 | if err != nil { 25 | hlog.Fatalf("new consul client failed: %s", err.Error()) 26 | } 27 | // init OpenTelemetry 28 | provider.NewOpenTelemetryProvider( 29 | provider.WithServiceName(config.GlobalServerConfig.ProfileSrvInfo.Name), 30 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 31 | provider.WithInsecure(), 32 | ) 33 | // create a new client 34 | c, err := profileservice.NewClient( 35 | config.GlobalServerConfig.ProfileSrvInfo.Name, 36 | client.WithResolver(r), // service discovery 37 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 38 | client.WithMuxConnection(1), // multiplexing 39 | client.WithSuite(tracing.NewClientSuite()), 40 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.ProfileSrvInfo.Name}), 41 | ) 42 | if err != nil { 43 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 44 | } 45 | config.ProfileClient = c 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/trip/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/registry" 12 | "github.com/cloudwego/kitex/pkg/utils" 13 | "github.com/hashicorp/consul/api" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry(Port int) (registry.Registry, *registry.Info) { 19 | r, err := consul.NewConsulRegister(net.JoinHostPort( 20 | config.GlobalConsulConfig.Host, 21 | strconv.Itoa(config.GlobalConsulConfig.Port)), 22 | consul.WithCheck(&api.AgentServiceCheck{ 23 | Interval: consts.ConsulCheckInterval, 24 | Timeout: consts.ConsulCheckTimeout, 25 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 26 | })) 27 | if err != nil { 28 | klog.Fatalf("new consul register failed: %s", err.Error()) 29 | } 30 | 31 | // Using snowflake to generate service name. 32 | sf, err := snowflake.NewNode(4) 33 | if err != nil { 34 | klog.Fatalf("generate service name failed: %s", err.Error()) 35 | } 36 | info := ®istry.Info{ 37 | ServiceName: config.GlobalServerConfig.Name, 38 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, strconv.Itoa(Port))), 39 | Tags: map[string]string{ 40 | "ID": sf.Generate().Base36(), 41 | }, 42 | } 43 | return r, info 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/trip/kitex.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'trip' 3 | ToolVersion: 'v0.4.4' 4 | -------------------------------------------------------------------------------- /server/cmd/trip/pkg/car/car.go: -------------------------------------------------------------------------------- 1 | package car 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/id" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 10 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car" 11 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car/carservice" 12 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 13 | ) 14 | 15 | // Manager defines a car manager. 16 | type Manager struct { 17 | CarService carservice.Client 18 | } 19 | 20 | // Verify verifies car status. 21 | func (m *Manager) Verify(c context.Context, cid id.CarID, aid id.AccountID) error { 22 | resp, err := m.CarService.GetCar(c, &car.GetCarRequest{ 23 | Id: cid.String(), 24 | AccountId: aid.String(), 25 | }) 26 | if err != nil { 27 | return errno.RPCCarSrvErr 28 | } 29 | if err = tools.ParseBaseResp(resp.BaseResp); err != nil { 30 | return err 31 | } 32 | if resp.Car.Status != base.CarStatus_LOCKED { 33 | return errno.BadRequest.WithMessage(fmt.Sprintf("cannot unlock;car status is %v", resp.Car.Status)) 34 | } 35 | return nil 36 | } 37 | 38 | // Unlock unlocks a car. 39 | func (m *Manager) Unlock(c context.Context, cid id.CarID, aid id.AccountID, tid id.TripID, avatarURL string) error { 40 | resp, err := m.CarService.UnlockCar(c, &car.UnlockCarRequest{ 41 | Id: cid.String(), 42 | AccountId: aid.String(), 43 | Driver: &base.Driver{ 44 | Id: aid.String(), 45 | AvatarUrl: avatarURL, 46 | }, 47 | TripId: tid.String(), 48 | }) 49 | if err != nil { 50 | return errno.RPCCarSrvErr 51 | } 52 | return tools.ParseBaseResp(resp.BaseResp) 53 | } 54 | 55 | // Lock locks a car. 56 | func (m *Manager) Lock(c context.Context, cid id.CarID, aid id.AccountID) error { 57 | resp, err := m.CarService.LockCar(c, &car.LockCarRequest{ 58 | AccountId: aid.String(), 59 | Id: cid.String(), 60 | }) 61 | if err != nil { 62 | return errno.RPCCarSrvErr 63 | } 64 | return tools.ParseBaseResp(resp.BaseResp) 65 | } 66 | -------------------------------------------------------------------------------- /server/cmd/trip/pkg/mq/model/PayInfo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/CyanAsterisk/FreeCar/server/shared/id" 4 | 5 | type PayInfo struct { 6 | AccountId id.AccountID `json:"account_id"` 7 | TripId id.TripID `json:"trip_id"` 8 | FeeCent int32 `json:"fee_cent"` 9 | } 10 | -------------------------------------------------------------------------------- /server/cmd/trip/pkg/mq/mq.go: -------------------------------------------------------------------------------- 1 | package mq 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/trip/pkg/mq/model" 7 | ) 8 | 9 | // Publisher defines the publishing interface. 10 | type Publisher interface { 11 | Publish(context.Context, *model.PayInfo) error 12 | } 13 | -------------------------------------------------------------------------------- /server/cmd/trip/pkg/poi/poi.go: -------------------------------------------------------------------------------- 1 | package poi 2 | 3 | import ( 4 | "hash/fnv" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 7 | "github.com/bytedance/sonic" 8 | ) 9 | 10 | var poi = []string{ 11 | "知行苑7舍", 12 | "兴业苑5舍", 13 | "中心食堂", 14 | "第二教学楼", 15 | "综合实验大楼", 16 | "信科大厦", 17 | } 18 | 19 | // Manager defines a poi manager. 20 | type Manager struct{} 21 | 22 | // Resolve resolves the given location. 23 | func (*Manager) Resolve(loc *base.Location) (string, error) { 24 | b, err := sonic.Marshal(loc) 25 | if err != nil { 26 | return "", err 27 | } 28 | 29 | h := fnv.New32() 30 | h.Write(b) 31 | return poi[int(h.Sum32())%len(poi)], nil 32 | } 33 | -------------------------------------------------------------------------------- /server/cmd/trip/pkg/profile/profile.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | import ( 4 | "context" 5 | "encoding/base64" 6 | "fmt" 7 | 8 | "github.com/CyanAsterisk/FreeCar/server/shared/id" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 10 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile" 11 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile/profileservice" 12 | "github.com/bytedance/sonic" 13 | ) 14 | 15 | // Manager defines a profile manager. 16 | type Manager struct { 17 | ProfileService profileservice.Client 18 | } 19 | 20 | // Verify verifies account identity. 21 | func (m *Manager) Verify(c context.Context, aid id.AccountID) (id.IdentityID, error) { 22 | nilID := id.IdentityID("") 23 | resp, err := m.ProfileService.GetProfile(c, &profile.GetProfileRequest{AccountId: aid.String()}) 24 | if err != nil { 25 | return nilID, fmt.Errorf("cannot get profile: %v", err) 26 | } 27 | if resp.Profile.IdentityStatus != base.IdentityStatus_VERIFIED { 28 | return nilID, fmt.Errorf("invalid identity status") 29 | } 30 | 31 | b, err := sonic.Marshal(resp.Profile.Identity) 32 | if err != nil { 33 | return nilID, fmt.Errorf("cannot marshal identity:%v", err) 34 | } 35 | return id.IdentityID(base64.StdEncoding.EncodeToString(b)), nil 36 | } 37 | -------------------------------------------------------------------------------- /server/cmd/user/Makefile: -------------------------------------------------------------------------------- 1 | server: 2 | kitex -service user -module github.com/CyanAsterisk/FreeCar -use github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen ./../../idl/rpc/user.thrift 3 | rm build.sh 4 | rm -rf script -------------------------------------------------------------------------------- /server/cmd/user/config.yaml: -------------------------------------------------------------------------------- 1 | host: '127.0.0.1' 2 | port: 8500 3 | key: 'freecar/user_srv' -------------------------------------------------------------------------------- /server/cmd/user/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type MysqlConfig struct { 4 | Host string `mapstructure:"host" json:"host"` 5 | Port int `mapstructure:"port" json:"port"` 6 | Name string `mapstructure:"db" json:"db"` 7 | User string `mapstructure:"user" json:"user"` 8 | Password string `mapstructure:"password" json:"password"` 9 | Salt string `mapstructure:"salt" json:"salt"` 10 | } 11 | 12 | type ConsulConfig struct { 13 | Host string `mapstructure:"host" json:"host"` 14 | Port int `mapstructure:"port" json:"port"` 15 | Key string `mapstructure:"key" json:"key"` 16 | } 17 | 18 | type OtelConfig struct { 19 | EndPoint string `mapstructure:"endpoint" json:"endpoint"` 20 | } 21 | 22 | type WXConfig struct { 23 | AppId string `mapstructure:"app_id" json:"app_id"` 24 | AppSecret string `mapstructure:"app_secret" json:"app_secret"` 25 | } 26 | 27 | type PasetoConfig struct { 28 | SecretKey string `mapstructure:"secret_key" json:"secret_key"` 29 | Implicit string `mapstructure:"implicit" json:"implicit"` 30 | } 31 | 32 | type ServerConfig struct { 33 | Name string `mapstructure:"name" json:"name"` 34 | Host string `mapstructure:"host" json:"host"` 35 | PasetoInfo PasetoConfig `mapstructure:"paseto" json:"paseto"` 36 | MysqlInfo MysqlConfig `mapstructure:"mysql" json:"mysql"` 37 | OtelInfo OtelConfig `mapstructure:"otel" json:"otel"` 38 | WXInfo WXConfig `mapstructure:"wx_config" json:"wx_config"` 39 | BlobSrvInfo BlobSrvConfig `mapstructure:"blob_srv" json:"blob_srv"` 40 | } 41 | 42 | type BlobSrvConfig struct { 43 | Name string `mapstructure:"name" json:"name"` 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/user/config/global.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | var ( 4 | GlobalServerConfig ServerConfig 5 | GlobalConsulConfig ConsulConfig 6 | ) 7 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/blob_service.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/cmd/user/config" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob/blobservice" 8 | "github.com/cloudwego/hertz/pkg/common/hlog" 9 | "github.com/cloudwego/kitex/client" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/loadbalance" 12 | "github.com/cloudwego/kitex/pkg/rpcinfo" 13 | "github.com/kitex-contrib/obs-opentelemetry/provider" 14 | "github.com/kitex-contrib/obs-opentelemetry/tracing" 15 | consul "github.com/kitex-contrib/registry-consul" 16 | ) 17 | 18 | // InitBlob to init blob service 19 | func InitBlob() blobservice.Client { 20 | // init resolver 21 | r, err := consul.NewConsulResolver(fmt.Sprintf("%s:%d", 22 | config.GlobalConsulConfig.Host, 23 | config.GlobalConsulConfig.Port)) 24 | if err != nil { 25 | hlog.Fatalf("new consul client failed: %s", err.Error()) 26 | } 27 | provider.NewOpenTelemetryProvider( 28 | provider.WithServiceName(config.GlobalServerConfig.BlobSrvInfo.Name), 29 | provider.WithExportEndpoint(config.GlobalServerConfig.OtelInfo.EndPoint), 30 | provider.WithInsecure(), 31 | ) 32 | 33 | // create a new client 34 | c, err := blobservice.NewClient( 35 | config.GlobalServerConfig.BlobSrvInfo.Name, 36 | client.WithResolver(r), // service discovery 37 | client.WithLoadBalancer(loadbalance.NewWeightedBalancer()), // load balance 38 | client.WithMuxConnection(1), // multiplexing 39 | client.WithSuite(tracing.NewClientSuite()), 40 | client.WithClientBasicInfo(&rpcinfo.EndpointBasicInfo{ServiceName: config.GlobalServerConfig.BlobSrvInfo.Name}), 41 | ) 42 | if err != nil { 43 | klog.Fatalf("ERROR: cannot init client: %v\n", err) 44 | } 45 | return c 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/config.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/user/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 10 | "github.com/bytedance/sonic" 11 | "github.com/cloudwego/hertz/pkg/common/hlog" 12 | "github.com/hashicorp/consul/api" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | // InitConfig to init consul config server 17 | func InitConfig() { 18 | v := viper.New() 19 | v.SetConfigFile(consts.UserConfigPath) 20 | if err := v.ReadInConfig(); err != nil { 21 | hlog.Fatalf("read viper config failed: %s", err.Error()) 22 | } 23 | if err := v.Unmarshal(&config.GlobalConsulConfig); err != nil { 24 | hlog.Fatalf("unmarshal err failed: %s", err.Error()) 25 | } 26 | hlog.Infof("Config Info: %v", config.GlobalConsulConfig) 27 | 28 | cfg := api.DefaultConfig() 29 | cfg.Address = net.JoinHostPort( 30 | config.GlobalConsulConfig.Host, 31 | strconv.Itoa(config.GlobalConsulConfig.Port)) 32 | consulClient, err := api.NewClient(cfg) 33 | if err != nil { 34 | hlog.Fatalf("new consul client failed: %s", err.Error()) 35 | } 36 | content, _, err := consulClient.KV().Get(config.GlobalConsulConfig.Key, nil) 37 | if err != nil { 38 | hlog.Fatalf("consul kv failed: %s", err.Error()) 39 | } 40 | 41 | err = sonic.Unmarshal(content.Value, &config.GlobalServerConfig) 42 | if err != nil { 43 | hlog.Fatalf("sonic unmarshal config failed: %s", err.Error()) 44 | } 45 | 46 | if config.GlobalServerConfig.Host == "" { 47 | config.GlobalServerConfig.Host, err = tools.GetLocalIPv4Address() 48 | if err != nil { 49 | hlog.Fatalf("get localIpv4Addr failed:%s", err.Error()) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/db.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/user/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/cloudwego/kitex/pkg/klog" 10 | "gorm.io/driver/mysql" 11 | "gorm.io/gorm" 12 | "gorm.io/gorm/logger" 13 | "gorm.io/gorm/schema" 14 | "gorm.io/plugin/opentelemetry/logging/logrus" 15 | "gorm.io/plugin/opentelemetry/tracing" 16 | ) 17 | 18 | // InitDB to init database 19 | func InitDB() *gorm.DB { 20 | c := config.GlobalServerConfig.MysqlInfo 21 | dsn := fmt.Sprintf(consts.MySqlDSN, c.User, c.Password, c.Host, c.Port, c.Name) 22 | newLogger := logger.New( 23 | logrus.NewWriter(), // io writer 24 | logger.Config{ 25 | SlowThreshold: time.Second, // Slow SQL Threshold 26 | LogLevel: logger.Silent, // Log level 27 | Colorful: true, // Disable color printing 28 | }, 29 | ) 30 | 31 | // global mode 32 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{ 33 | NamingStrategy: schema.NamingStrategy{ 34 | SingularTable: true, 35 | }, 36 | Logger: newLogger, 37 | }) 38 | if err != nil { 39 | klog.Fatalf("init gorm failed: %s", err.Error()) 40 | } 41 | 42 | if err = db.Use(tracing.NewPlugin()); err != nil { 43 | klog.Fatalf("use tracing plugin failed: %s", err.Error()) 44 | } 45 | return db 46 | } 47 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/flag.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "flag" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/tools" 8 | "github.com/cloudwego/kitex/pkg/klog" 9 | ) 10 | 11 | // InitFlag to init flag 12 | func InitFlag() (string, int) { 13 | IP := flag.String(consts.IPFlagName, consts.IPFlagValue, consts.IPFlagUsage) 14 | Port := flag.Int(consts.PortFlagName, 0, consts.PortFlagUsage) 15 | // Parsing flags and if Port is 0 , then will automatically get an empty Port. 16 | flag.Parse() 17 | if *Port == 0 { 18 | *Port, _ = tools.GetFreePort() 19 | } 20 | klog.Info("ip: ", *IP) 21 | klog.Info("port: ", *Port) 22 | return *IP, *Port 23 | } 24 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/logger.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "os" 5 | "path" 6 | "runtime" 7 | "time" 8 | 9 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | kitexlogrus "github.com/kitex-contrib/obs-opentelemetry/logging/logrus" 12 | "gopkg.in/natefinch/lumberjack.v2" 13 | ) 14 | 15 | // InitLogger to init logrus 16 | func InitLogger() { 17 | // Customizable output directory. 18 | logFilePath := consts.KlogFilePath 19 | if err := os.MkdirAll(logFilePath, 0o777); err != nil { 20 | panic(err) 21 | } 22 | 23 | // Set filename to date 24 | logFileName := time.Now().Format("2006-01-02") + ".log" 25 | fileName := path.Join(logFilePath, logFileName) 26 | if _, err := os.Stat(fileName); err != nil { 27 | if _, err := os.Create(fileName); err != nil { 28 | panic(err) 29 | } 30 | } 31 | 32 | logger := kitexlogrus.NewLogger() 33 | // Provides compression and deletion 34 | lumberjackLogger := &lumberjack.Logger{ 35 | Filename: fileName, 36 | MaxSize: 20, // A file can be up to 20M. 37 | MaxBackups: 5, // Save up to 5 files at the same time. 38 | MaxAge: 10, // A file can exist for a maximum of 10 days. 39 | Compress: true, // Compress with gzip. 40 | } 41 | 42 | if runtime.GOOS == "linux" { 43 | logger.SetOutput(lumberjackLogger) 44 | logger.SetLevel(klog.LevelWarn) 45 | } else { 46 | logger.SetLevel(klog.LevelDebug) 47 | } 48 | 49 | klog.SetLogger(logger) 50 | } 51 | -------------------------------------------------------------------------------- /server/cmd/user/initialize/registry.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | "net" 5 | "strconv" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/cmd/user/config" 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | "github.com/bwmarrin/snowflake" 10 | "github.com/cloudwego/kitex/pkg/klog" 11 | "github.com/cloudwego/kitex/pkg/registry" 12 | "github.com/cloudwego/kitex/pkg/utils" 13 | "github.com/hashicorp/consul/api" 14 | consul "github.com/kitex-contrib/registry-consul" 15 | ) 16 | 17 | // InitRegistry to init consul 18 | func InitRegistry(Port int) (registry.Registry, *registry.Info) { 19 | r, err := consul.NewConsulRegister(net.JoinHostPort( 20 | config.GlobalConsulConfig.Host, 21 | strconv.Itoa(config.GlobalConsulConfig.Port)), 22 | consul.WithCheck(&api.AgentServiceCheck{ 23 | Interval: consts.ConsulCheckInterval, 24 | Timeout: consts.ConsulCheckTimeout, 25 | DeregisterCriticalServiceAfter: consts.ConsulCheckDeregisterCriticalServiceAfter, 26 | })) 27 | if err != nil { 28 | klog.Fatalf("new consul register failed: %s", err.Error()) 29 | } 30 | 31 | // Using snowflake to generate service name. 32 | sf, err := snowflake.NewNode(2) 33 | if err != nil { 34 | klog.Fatalf("generate service name failed: %s", err.Error()) 35 | } 36 | info := ®istry.Info{ 37 | ServiceName: config.GlobalServerConfig.Name, 38 | Addr: utils.NewNetAddr(consts.TCP, net.JoinHostPort(config.GlobalServerConfig.Host, strconv.Itoa(Port))), 39 | Tags: map[string]string{ 40 | "ID": sf.Generate().Base36(), 41 | }, 42 | } 43 | return r, info 44 | } 45 | -------------------------------------------------------------------------------- /server/cmd/user/kitex.yaml: -------------------------------------------------------------------------------- 1 | kitexinfo: 2 | ServiceName: 'user' 3 | ToolVersion: 'v0.4.4' 4 | -------------------------------------------------------------------------------- /server/cmd/user/pkg/md5/md5.go: -------------------------------------------------------------------------------- 1 | package md5 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | type EncryptManager struct { 10 | Salt string 11 | } 12 | 13 | func (e *EncryptManager) EncryptPassword(code string) string { 14 | return Md5Crypt(code, e.Salt) 15 | } 16 | 17 | // Md5Crypt uses MD5 encryption algorithm to add salt encryption. 18 | func Md5Crypt(str string, salt ...interface{}) (CryptStr string) { 19 | if l := len(salt); l > 0 { 20 | slice := make([]string, l+1) 21 | str = fmt.Sprintf(str+strings.Join(slice, "%v"), salt...) 22 | } 23 | return fmt.Sprintf("%x", md5.Sum([]byte(str))) 24 | } 25 | -------------------------------------------------------------------------------- /server/cmd/user/pkg/mysql/admin_test.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/test" 8 | ) 9 | 10 | func TestUserLifeCycle(t *testing.T) { 11 | cleanUp, db, err := test.RunWithMySQLInDocker(t) 12 | defer cleanUp() 13 | if err != nil { 14 | t.Fatal(err) 15 | } 16 | salt := "test-salt" 17 | 18 | admin := Admin{ 19 | ID: "1234", 20 | Username: "username1", 21 | Password: "password-1", 22 | } 23 | manager := NewAdminManager(db, salt) 24 | db.Create(&admin) 25 | cases := []struct { 26 | name string 27 | op func() string 28 | want string 29 | }{ 30 | { 31 | name: "get admin by name", 32 | op: func() string { 33 | u := admin 34 | resp, err := manager.GetAdminByName(u.Username) 35 | return fmt.Sprintf("[err = %+v][resp = %+v]", err, resp) 36 | }, 37 | want: "[err = ][resp = &{ID:1234 Username:username1 Password:password-1}]", 38 | }, 39 | { 40 | name: "get admin by AccountId", 41 | op: func() string { 42 | u := admin 43 | resp, err := manager.GetAdminByAccountId(u.ID) 44 | return fmt.Sprintf("[err = %+v][resp = %+v]", err, resp) 45 | }, 46 | want: "[err = ][resp = &{ID:1234 Username:username1 Password:password-1}]", 47 | }, 48 | { 49 | name: "update admin password", 50 | op: func() string { 51 | u := admin 52 | err = manager.UpdateAdminPassword(u.ID, "newPassword-1") 53 | if err != nil { 54 | return fmt.Sprintf("[err=%+v]", err) 55 | } 56 | resp, err := manager.GetAdminByAccountId(u.ID) 57 | return fmt.Sprintf("[err = %+v][resp = %+v]", err, resp) 58 | }, 59 | want: "[err = ][resp = &{ID:1234 Username:username1 Password:386ff2339004f5dc8dc0cb5aa60aa94e}]", 60 | }, 61 | } 62 | 63 | for _, cc := range cases { 64 | got := cc.op() 65 | if got != cc.want { 66 | t.Errorf("%s failed: want: %s,got %s", cc.name, cc.want, got) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /server/cmd/user/pkg/paseto/paseto.go: -------------------------------------------------------------------------------- 1 | package paseto 2 | 3 | import ( 4 | "github.com/hertz-contrib/paseto" 5 | ) 6 | 7 | type TokenGenerator struct { 8 | paseto.GenTokenFunc 9 | } 10 | 11 | func NewTokenGenerator(asymmetricKey string, implicit []byte) (*TokenGenerator, error) { 12 | signFunc, err := paseto.NewV4SignFunc(asymmetricKey, implicit) 13 | if err != nil { 14 | return nil, err 15 | } 16 | return &TokenGenerator{signFunc}, nil 17 | } 18 | 19 | func (g *TokenGenerator) CreateToken(claims *paseto.StandardClaims) (token string, err error) { 20 | return g.GenTokenFunc(claims, nil, nil) 21 | } 22 | -------------------------------------------------------------------------------- /server/cmd/user/pkg/wechat/wechat.go: -------------------------------------------------------------------------------- 1 | package wechat 2 | 3 | import ( 4 | "github.com/cloudwego/kitex/pkg/klog" 5 | "github.com/medivhzhan/weapp/v2" 6 | ) 7 | 8 | type AuthServiceImpl struct { 9 | AppID string 10 | AppSecret string 11 | } 12 | 13 | // Resolve resolves authorization code to WeChat open id,return empty string if an Error occurs. 14 | func (s *AuthServiceImpl) Resolve(code string) string { 15 | resp, err := weapp.Login(s.AppID, s.AppSecret, code) 16 | if err != nil { 17 | klog.Errorf("WeApp.Login Err: %v code:%s", err, code) 18 | return "" 19 | } 20 | if err := resp.GetResponseError(); err != nil { 21 | klog.Errorf("WeApp.Login resp Err: %v code:%s", err, code) 22 | return "" 23 | } 24 | return resp.OpenID 25 | } 26 | -------------------------------------------------------------------------------- /server/idl/base/car.thrift: -------------------------------------------------------------------------------- 1 | namespace go base 2 | 3 | struct CarEntity { 4 | 1: string id, 5 | 2: Car car, 6 | } 7 | 8 | enum CarStatus { 9 | CS_NOT_SPECIFIED = 0, 10 | LOCKED = 1, 11 | UNLOCKING = 2, 12 | UNLOCKED = 3, 13 | LOCKING = 4, 14 | } 15 | 16 | struct Driver { 17 | 1: string id, 18 | 2: string avatar_url, 19 | } 20 | 21 | struct Position { 22 | 1: double latitude, 23 | 2: double longitude, 24 | } 25 | 26 | struct Car { 27 | 1: CarStatus status, 28 | 2: Driver driver, 29 | 3: Position position, 30 | 4: string trip_id, 31 | 5: double power, 32 | 6: string plate_num, 33 | } 34 | -------------------------------------------------------------------------------- /server/idl/base/common.thrift: -------------------------------------------------------------------------------- 1 | namespace go base 2 | 3 | struct BaseResponse { 4 | 1: i64 status_code, // Status code, 0-success, other values-failure 5 | 2: string status_msg, // Return status description 6 | } 7 | 8 | struct NilResponse {} -------------------------------------------------------------------------------- /server/idl/base/errno.thrift: -------------------------------------------------------------------------------- 1 | namespace go errno 2 | 3 | enum Err { 4 | Success = 0, 5 | NoRoute = 1, 6 | NoMethod = 2, 7 | BadRequest = 10000, 8 | ParamsErr = 10001, 9 | AuthorizeFail = 10002, 10 | TooManyRequest = 10003, 11 | ServiceErr = 20000, 12 | RPCUserSrvErr = 30000, 13 | UserSrvErr = 30001, 14 | RPCBlobSrvErr = 40000, 15 | BlobSrvErr = 40001, 16 | RPCCarSrvErr = 50000, 17 | CarSrvErr = 50001, 18 | RPCProfileSrvErr = 60000, 19 | ProfileSrvErr = 60001, 20 | RPCTripSrvErr = 70000, 21 | TripSrvErr = 70001, 22 | RecordNotFound = 80000, 23 | RecordAlreadyExist = 80001, 24 | DirtyData = 80003, 25 | TradeSrvErr = 90001, 26 | } -------------------------------------------------------------------------------- /server/idl/base/profile.thrift: -------------------------------------------------------------------------------- 1 | namespace go base 2 | 3 | // Profile Service 4 | enum Gender { 5 | G_NOT_SPECIFIED = 0, 6 | MALE = 1, 7 | FEMALE = 2, 8 | } 9 | 10 | enum IdentityStatus { 11 | UNSUBMITTED = 0, 12 | PENDING = 1, 13 | VERIFIED = 2, 14 | AUDITFAILED = 3, 15 | } 16 | 17 | struct ProfileRecord { 18 | 1: string account_id, 19 | 2: string photo_blob_id, 20 | 3: Profile profile, 21 | } 22 | 23 | struct Profile { 24 | 1: Identity identity, 25 | 2: IdentityStatus identity_status, 26 | } 27 | 28 | struct Identity { 29 | 1: string lic_number, 30 | 2: string name, 31 | 3: Gender gender, 32 | 4: i64 birth_date_millis, 33 | } -------------------------------------------------------------------------------- /server/idl/base/trip.thrift: -------------------------------------------------------------------------------- 1 | namespace go base 2 | 3 | // Trip Service 4 | struct Location { 5 | 1: double latitude, 6 | 2: double longitude, 7 | } 8 | 9 | struct LocationStatus { 10 | 1: Location location, 11 | 2: i32 fee_cent, 12 | 3: double km_driven, 13 | 4: string poi_name, 14 | 5: i64 timestamp_sec, 15 | } 16 | 17 | enum TripStatus { 18 | TS_NOT_SPECIFIED = 0, 19 | IN_PROGRESS = 1, 20 | FINISHED = 2, 21 | } 22 | 23 | struct TripEntity { 24 | 1: string id, 25 | 2: Trip trip, 26 | } 27 | 28 | struct Trip { 29 | 1: string account_id, 30 | 2: string car_id, 31 | 3: LocationStatus start, 32 | 4: LocationStatus current, 33 | 5: LocationStatus end, 34 | 6: TripStatus status, 35 | 7: string identity_id, 36 | } -------------------------------------------------------------------------------- /server/idl/base/user.thrift: -------------------------------------------------------------------------------- 1 | namespace go base 2 | 3 | struct User{ 4 | 1: string account_id; 5 | 2: string username; 6 | 3: string phone_number; 7 | 4: string avatar_blob_id; 8 | 5: string open_id; 9 | 6: i32 balance; 10 | } 11 | 12 | struct UserInfo{ 13 | 1: string account_id; 14 | 2: string username; 15 | 3: string phone_number; 16 | 4: string avatar_url; 17 | 5: i32 balance; 18 | } -------------------------------------------------------------------------------- /server/idl/http/car.thrift: -------------------------------------------------------------------------------- 1 | namespace go car 2 | 3 | include "../base/common.thrift" 4 | include "../base/car.thrift" 5 | 6 | struct AdminCreateCarRequest { 7 | 1: string plate_num (api.raw = "plate_num", api.vd = "len($) > 0 && len($) < 25>"), 8 | } 9 | 10 | struct AdminDeleteCarRequest { 11 | 1: string id (api.raw = "id", api.vd = "len($) > 0 && len($) < 25>"), 12 | } 13 | 14 | struct AdminGetSomeCarsRequest {} 15 | 16 | struct AdminGetAllCarsRequest {} 17 | 18 | struct GetCarsRequest {} 19 | 20 | struct GetCarRequest { 21 | 1: string id (api.raw = "id", api.vd = "len($) > 0 && len($) < 25>"), 22 | } 23 | 24 | service CarService { 25 | // for back-stage management 26 | common.NilResponse AdminCreateCar(1: AdminCreateCarRequest req) (api.post = "/admin/car"), 27 | common.NilResponse AdminDeleteCar(1: AdminDeleteCarRequest req) (api.delete = "/admin/car"), 28 | common.NilResponse AdminGetSomeCars(1: AdminGetSomeCarsRequest req) (api.get = "/admin/car/some"), 29 | common.NilResponse AdminGetAllCars(1: AdminGetAllCarsRequest req) (api.get = "/admin/car/all"), 30 | 31 | // for mini-program 32 | common.NilResponse GetCars(1: GetCarsRequest req) (api.get = "/cars"), 33 | common.NilResponse GetCar(1: GetCarRequest req) (api.get = "/car"), 34 | } -------------------------------------------------------------------------------- /server/idl/http/gpt.thrift: -------------------------------------------------------------------------------- 1 | namespace go gpt 2 | 3 | include "../base/common.thrift" 4 | 5 | struct ChatRequest { 6 | 1: string content 7 | } 8 | 9 | struct ChatResponse { 10 | 1: common.BaseResponse base_resp 11 | 2: string content 12 | } 13 | 14 | service GptService { 15 | ChatResponse Chat(1: ChatRequest req) (api.post = "/chat"), 16 | } -------------------------------------------------------------------------------- /server/idl/http/trip.thrift: -------------------------------------------------------------------------------- 1 | namespace go trip 2 | 3 | include "../base/common.thrift" 4 | include "../base/trip.thrift" 5 | 6 | struct CreateTripRequest { 7 | 1: trip.Location start (api.raw = "start"), 8 | 2: string car_id (api.raw = "car_id"), 9 | 3: string avatar_url (api.raw = "avatar_url"), 10 | } 11 | 12 | struct GetTripRequest { 13 | 1: string id (api.raw = "id"), 14 | } 15 | 16 | struct GetTripsRequest { 17 | 1: trip.TripStatus status (api.raw = "status"), 18 | } 19 | 20 | struct UpdateTripRequest { 21 | 1: string id (api.raw = "id"), 22 | 2: trip.Location current (api.raw = "current"), 23 | 3: bool end_trip (api.raw = "end_trip"), 24 | } 25 | 26 | struct DeleteTripRequest { 27 | 1: string id (api.raw = "id"), 28 | } 29 | 30 | struct GetAllTripsRequest {} 31 | 32 | struct GetSomeTripsRequest {} 33 | 34 | service TripService { 35 | // for back-stage management 36 | common.NilResponse DeleteTrip(1: DeleteTripRequest req) (api.delete = "/admin/trip"), 37 | common.NilResponse GetAllTrips(1: GetAllTripsRequest req) (api.get = "/admin/trip/all"), 38 | common.NilResponse GetSomeTrips(1: GetSomeTripsRequest req) (api.get = "/admin/trip/some"), 39 | 40 | // for mini-program 41 | common.NilResponse CreateTrip(1: CreateTripRequest req) (api.post = "/trip"), 42 | common.NilResponse GetTrip(1: GetTripRequest req) (api.get = "/trip"), 43 | common.NilResponse GetTrips(1: GetTripsRequest req) (api.get = "/trips"), 44 | common.NilResponse UpdateTrip(1: UpdateTripRequest req) (api.put = "/trip"), 45 | } -------------------------------------------------------------------------------- /server/idl/rpc/blob.thrift: -------------------------------------------------------------------------------- 1 | namespace go blob 2 | 3 | include "../base/common.thrift" 4 | 5 | struct CreateBlobRequest { 6 | 1: string account_id, 7 | 2: i32 upload_url_timeout_sec, 8 | } 9 | 10 | struct CreateBlobResponse { 11 | 1: common.BaseResponse base_resp, 12 | 2: string id, 13 | 3: string upload_url, 14 | } 15 | 16 | struct GetBlobURLRequest { 17 | 1: string id, 18 | 2: i32 timeout_sec, 19 | } 20 | 21 | struct GetBlobURLResponse { 22 | 1: common.BaseResponse base_resp, 23 | 2: string url, 24 | } 25 | 26 | service BlobService { 27 | CreateBlobResponse CreateBlob(1: CreateBlobRequest req), 28 | GetBlobURLResponse GetBlobURL(1: GetBlobURLRequest req), 29 | } -------------------------------------------------------------------------------- /server/idl/rpc/trip.thrift: -------------------------------------------------------------------------------- 1 | namespace go trip 2 | 3 | include "../base/common.thrift" 4 | include "../base/trip.thrift" 5 | 6 | struct CreateTripRequest { 7 | 1: trip.Location start, 8 | 2: string car_id, 9 | 3: string avatar_url, 10 | 4: string account_id, 11 | } 12 | 13 | struct CreateTripResponse { 14 | 1: common.BaseResponse base_resp, 15 | 2: trip.TripEntity trip_entity, 16 | } 17 | 18 | struct GetTripRequest { 19 | 1: string id, 20 | 2: string account_id, 21 | } 22 | 23 | struct GetTripResponse { 24 | 1: common.BaseResponse base_resp, 25 | 2: trip.Trip trip, 26 | } 27 | 28 | struct GetTripsRequest { 29 | 1: trip.TripStatus status, 30 | 2: string account_id, 31 | } 32 | 33 | struct GetTripsResponse { 34 | 1: common.BaseResponse base_resp, 35 | 2: list trips, 36 | } 37 | 38 | struct UpdateTripRequest { 39 | 1: string id, 40 | 2: trip.Location current, 41 | 3: bool end_trip, 42 | 4: string account_id, 43 | } 44 | 45 | struct UpdateTripResponse { 46 | 1: common.BaseResponse base_resp, 47 | 2: trip.Trip trip, 48 | } 49 | 50 | struct DeleteTripRequest { 51 | 1: string id, 52 | } 53 | 54 | struct DeleteTripResponse { 55 | 1: common.BaseResponse base_resp, 56 | } 57 | 58 | struct GetAllTripsRequest { 59 | } 60 | 61 | struct GetAllTripsResponse { 62 | 1: common.BaseResponse base_resp, 63 | 2: list trips, 64 | } 65 | 66 | struct GetSomeTripsRequest { 67 | } 68 | 69 | struct GetSomeTripsResponse { 70 | 1: common.BaseResponse base_resp, 71 | 2: list trips, 72 | } 73 | 74 | service TripService { 75 | CreateTripResponse CreateTrip(1: CreateTripRequest req), 76 | GetTripResponse GetTrip(1: GetTripRequest req), 77 | GetTripsResponse GetTrips(1: GetTripsRequest req), 78 | UpdateTripResponse UpdateTrip(1: UpdateTripRequest req), 79 | GetAllTripsResponse GetAllTrips(1: GetAllTripsRequest req), 80 | GetSomeTripsResponse GetSomeTrips(1: GetSomeTripsRequest req), 81 | DeleteTripResponse DeleteTrip(1: DeleteTripRequest req), 82 | } -------------------------------------------------------------------------------- /server/shared/Makefile: -------------------------------------------------------------------------------- 1 | # eg. make service=video generate 2 | generate: 3 | kitex -module github.com/CyanAsterisk/FreeCar ./../idl/rpc/$(service).thrift 4 | 5 | user: 6 | make service=user generate 7 | blob: 8 | make service=blob generate 9 | car: 10 | make service=car generate 11 | profile: 12 | make service=profile generate 13 | trip: 14 | make service=trip generate 15 | errno: 16 | kitex -module github.com/CyanAsterisk/FreeCar ./../idl/base/errno.thrift 17 | 18 | all: 19 | make user 20 | make blob 21 | make car 22 | make profile 23 | make trip 24 | make errno 25 | -------------------------------------------------------------------------------- /server/shared/id/id.go: -------------------------------------------------------------------------------- 1 | package id 2 | 3 | // AccountID defines account id object. 4 | type AccountID string 5 | 6 | func (a AccountID) String() string { 7 | return string(a) 8 | } 9 | 10 | // TripID defines trip id object. 11 | type TripID string 12 | 13 | func (t TripID) String() string { 14 | return string(t) 15 | } 16 | 17 | // IdentityID defines identity id object. 18 | type IdentityID string 19 | 20 | func (i IdentityID) String() string { 21 | return string(i) 22 | } 23 | 24 | // CarID defines car id object. 25 | type CarID string 26 | 27 | func (i CarID) String() string { 28 | return string(i) 29 | } 30 | 31 | // BlobID defines blob id object. 32 | type BlobID string 33 | 34 | func (i BlobID) String() string { 35 | return string(i) 36 | } 37 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/base/k-consts.go: -------------------------------------------------------------------------------- 1 | package base 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/blob/blobservice/client.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package blobservice 4 | 5 | import ( 6 | "context" 7 | blob "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob" 8 | client "github.com/cloudwego/kitex/client" 9 | callopt "github.com/cloudwego/kitex/client/callopt" 10 | ) 11 | 12 | // Client is designed to provide IDL-compatible methods with call-option parameter for kitex framework. 13 | type Client interface { 14 | CreateBlob(ctx context.Context, req *blob.CreateBlobRequest, callOptions ...callopt.Option) (r *blob.CreateBlobResponse, err error) 15 | GetBlobURL(ctx context.Context, req *blob.GetBlobURLRequest, callOptions ...callopt.Option) (r *blob.GetBlobURLResponse, err error) 16 | } 17 | 18 | // NewClient creates a client for the service defined in IDL. 19 | func NewClient(destService string, opts ...client.Option) (Client, error) { 20 | var options []client.Option 21 | options = append(options, client.WithDestService(destService)) 22 | 23 | options = append(options, opts...) 24 | 25 | kc, err := client.NewClient(serviceInfo(), options...) 26 | if err != nil { 27 | return nil, err 28 | } 29 | return &kBlobServiceClient{ 30 | kClient: newServiceClient(kc), 31 | }, nil 32 | } 33 | 34 | // MustNewClient creates a client for the service defined in IDL. It panics if any error occurs. 35 | func MustNewClient(destService string, opts ...client.Option) Client { 36 | kc, err := NewClient(destService, opts...) 37 | if err != nil { 38 | panic(err) 39 | } 40 | return kc 41 | } 42 | 43 | type kBlobServiceClient struct { 44 | *kClient 45 | } 46 | 47 | func (p *kBlobServiceClient) CreateBlob(ctx context.Context, req *blob.CreateBlobRequest, callOptions ...callopt.Option) (r *blob.CreateBlobResponse, err error) { 48 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 49 | return p.kClient.CreateBlob(ctx, req) 50 | } 51 | 52 | func (p *kBlobServiceClient) GetBlobURL(ctx context.Context, req *blob.GetBlobURLRequest, callOptions ...callopt.Option) (r *blob.GetBlobURLResponse, err error) { 53 | ctx = client.NewCtxWithCallOptions(ctx, callOptions) 54 | return p.kClient.GetBlobURL(ctx, req) 55 | } 56 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/blob/blobservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package blobservice 4 | 5 | import ( 6 | blob "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob" 7 | server "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler blob.BlobService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/blob/blobservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | package blobservice 3 | 4 | import ( 5 | blob "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/blob" 6 | server "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler blob.BlobService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/blob/k-consts.go: -------------------------------------------------------------------------------- 1 | package blob 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/car/carservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package carservice 4 | 5 | import ( 6 | car "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car" 7 | server "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler car.CarService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/car/carservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | package carservice 3 | 4 | import ( 5 | car "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/car" 6 | server "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler car.CarService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/car/k-consts.go: -------------------------------------------------------------------------------- 1 | package car 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/errno/k-consts.go: -------------------------------------------------------------------------------- 1 | package errno 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/errno/k-errno.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package errno 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "reflect" 9 | "strings" 10 | 11 | "github.com/apache/thrift/lib/go/thrift" 12 | 13 | "github.com/cloudwego/kitex/pkg/protocol/bthrift" 14 | ) 15 | 16 | // unused protection 17 | var ( 18 | _ = fmt.Formatter(nil) 19 | _ = (*bytes.Buffer)(nil) 20 | _ = (*strings.Builder)(nil) 21 | _ = reflect.Type(nil) 22 | _ = thrift.TProtocol(nil) 23 | _ = bthrift.BinaryWriter(nil) 24 | ) 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/profile/k-consts.go: -------------------------------------------------------------------------------- 1 | package profile 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/profile/profileservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package profileservice 4 | 5 | import ( 6 | profile "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile" 7 | server "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler profile.ProfileService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/profile/profileservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | package profileservice 3 | 4 | import ( 5 | profile "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/profile" 6 | server "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler profile.ProfileService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/trip/k-consts.go: -------------------------------------------------------------------------------- 1 | package trip 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/trip/tripservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package tripservice 4 | 5 | import ( 6 | trip "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip" 7 | server "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler trip.TripService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/trip/tripservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | package tripservice 3 | 4 | import ( 5 | trip "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/trip" 6 | server "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler trip.TripService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/user/k-consts.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | // KitexUnusedProtection is used to prevent 'imported and not used' error. 4 | var KitexUnusedProtection = struct{}{} 5 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/user/userservice/invoker.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | 3 | package userservice 4 | 5 | import ( 6 | user "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/user" 7 | server "github.com/cloudwego/kitex/server" 8 | ) 9 | 10 | // NewInvoker creates a server.Invoker with the given handler and options. 11 | func NewInvoker(handler user.UserService, opts ...server.Option) server.Invoker { 12 | var options []server.Option 13 | 14 | options = append(options, opts...) 15 | 16 | s := server.NewInvoker(options...) 17 | if err := s.RegisterService(serviceInfo(), handler); err != nil { 18 | panic(err) 19 | } 20 | if err := s.Init(); err != nil { 21 | panic(err) 22 | } 23 | return s 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/kitex_gen/user/userservice/server.go: -------------------------------------------------------------------------------- 1 | // Code generated by Kitex v0.4.4. DO NOT EDIT. 2 | package userservice 3 | 4 | import ( 5 | user "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/user" 6 | server "github.com/cloudwego/kitex/server" 7 | ) 8 | 9 | // NewServer creates a server.Server with the given handler and options. 10 | func NewServer(handler user.UserService, opts ...server.Option) server.Server { 11 | var options []server.Option 12 | 13 | options = append(options, opts...) 14 | 15 | svr := server.NewServer(options...) 16 | if err := svr.RegisterService(serviceInfo(), handler); err != nil { 17 | panic(err) 18 | } 19 | return svr 20 | } 21 | -------------------------------------------------------------------------------- /server/shared/middleware/recovery.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 8 | "github.com/cloudwego/hertz/pkg/app" 9 | "github.com/cloudwego/hertz/pkg/app/middlewares/server/recovery" 10 | "github.com/cloudwego/hertz/pkg/common/hlog" 11 | "github.com/cloudwego/hertz/pkg/common/utils" 12 | "github.com/cloudwego/hertz/pkg/protocol/consts" 13 | ) 14 | 15 | func Recovery() app.HandlerFunc { 16 | return recovery.Recovery(recovery.WithRecoveryHandler( 17 | func(ctx context.Context, c *app.RequestContext, err interface{}, stack []byte) { 18 | hlog.SystemLogger().CtxErrorf(ctx, "[Recovery] err=%v\nstack=%s", err, stack) 19 | c.JSON(consts.StatusInternalServerError, utils.H{ 20 | "code": errno.BadRequest, 21 | "message": fmt.Sprintf("[Recovery] Panic"), 22 | }) 23 | }, 24 | )) 25 | } 26 | -------------------------------------------------------------------------------- /server/shared/mongo/mongo.go: -------------------------------------------------------------------------------- 1 | package mgutil 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/CyanAsterisk/FreeCar/server/shared/mongo/objid" 8 | "go.mongodb.org/mongo-driver/bson" 9 | "go.mongodb.org/mongo-driver/bson/primitive" 10 | ) 11 | 12 | // Common field names. 13 | const ( 14 | IDFieldName = "_id" 15 | UpdatedAtFieldName = "updatedat" 16 | ) 17 | 18 | // IDField defines the object id field. 19 | type IDField struct { 20 | ID primitive.ObjectID `bson:"_id"` 21 | } 22 | 23 | // UpdatedAtField defines the updatedat field. 24 | type UpdatedAtField struct { 25 | UpdatedAt int64 `bson:"updatedat"` 26 | } 27 | 28 | // NewObjID generates a new object id. 29 | var NewObjID = primitive.NewObjectID 30 | 31 | // NewObjIDWithValue sets id for next objectID generation. 32 | func NewObjIDWithValue(id fmt.Stringer) { 33 | NewObjID = func() primitive.ObjectID { 34 | return objid.MustFromID(id) 35 | } 36 | } 37 | 38 | // UpdatedAt returns a value suitable for UpdatedAt field. 39 | var UpdatedAt = func() int64 { 40 | return time.Now().UnixNano() 41 | } 42 | 43 | // SetNextUpdateAt sets id for next objectID generation. 44 | func SetNextUpdateAt(time int64) { 45 | UpdatedAt = func() int64 { 46 | return time 47 | } 48 | } 49 | 50 | // Set returns a $set update document. 51 | func Set(v interface{}) bson.M { 52 | return bson.M{ 53 | "$set": v, 54 | } 55 | } 56 | 57 | // ZeroOrDoesNotExist generates a filter expression with 58 | // field equal to zero or field does not exist. 59 | func ZeroOrDoesNotExist(field string, zero interface{}) bson.M { 60 | return bson.M{ 61 | "$or": []bson.M{ 62 | { 63 | field: zero, 64 | }, 65 | { 66 | field: bson.M{ 67 | "$exists": false, 68 | }, 69 | }, 70 | }, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /server/shared/mongo/objid/objid.go: -------------------------------------------------------------------------------- 1 | package objid 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/id" 7 | "go.mongodb.org/mongo-driver/bson/primitive" 8 | ) 9 | 10 | // FromID converts an id to objected id. 11 | func FromID(id fmt.Stringer) (primitive.ObjectID, error) { 12 | return primitive.ObjectIDFromHex(id.String()) 13 | } 14 | 15 | // MustFromID converts an id to objected id, panics on error. 16 | func MustFromID(id fmt.Stringer) primitive.ObjectID { 17 | oid, err := FromID(id) 18 | if err != nil { 19 | panic(err) 20 | } 21 | return oid 22 | } 23 | 24 | // ToTripID converts object id to trip id. 25 | func ToTripID(oid primitive.ObjectID) id.TripID { 26 | return id.TripID(oid.Hex()) 27 | } 28 | -------------------------------------------------------------------------------- /server/shared/mongo/setup.js: -------------------------------------------------------------------------------- 1 | use 2 | FreeCar 3 | 4 | db.trip.createIndex({ 5 | "trip.accountid": 1, 6 | "trip.status": 1, 7 | }, { 8 | unique: true, 9 | partialFilterExpression: { 10 | "trip.status": 1, 11 | } 12 | }) 13 | 14 | db.profile.createIndex({ 15 | "accountid": 1, 16 | }, { 17 | unique: true, 18 | }) -------------------------------------------------------------------------------- /server/shared/test/redis.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 9 | 10 | "github.com/docker/docker/api/types" 11 | "github.com/docker/docker/api/types/container" 12 | "github.com/docker/docker/client" 13 | "github.com/docker/go-connections/nat" 14 | "github.com/go-redis/redis/v8" 15 | ) 16 | 17 | // RunWithRedisInDocker runs the tests with 18 | // a redis instance in a docker container. 19 | func RunWithRedisInDocker(db int, t *testing.T) (cleanUpFunc func(), cli *redis.Client, err error) { 20 | c, err := client.NewClientWithOpts(client.WithVersion("1.41")) 21 | if err != nil { 22 | return func() {}, nil, err 23 | } 24 | 25 | ctx := context.Background() 26 | 27 | resp, err := c.ContainerCreate(ctx, 28 | &container.Config{ 29 | Image: consts.RedisImage, 30 | ExposedPorts: nat.PortSet{ 31 | consts.RedisContainerPort: {}, 32 | }, 33 | }, 34 | &container.HostConfig{ 35 | PortBindings: nat.PortMap{ 36 | consts.RedisContainerPort: []nat.PortBinding{ 37 | { 38 | HostIP: consts.RedisContainerIP, 39 | HostPort: consts.RedisPort, 40 | }, 41 | }, 42 | }, 43 | }, nil, nil, "") 44 | if err != nil { 45 | return func() {}, nil, err 46 | } 47 | containerID := resp.ID 48 | cleanUpFunc = func() { 49 | err = c.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{ 50 | Force: true, 51 | }) 52 | if err != nil { 53 | t.Error("remove test docker failed", err) 54 | } 55 | } 56 | 57 | err = c.ContainerStart(ctx, containerID, types.ContainerStartOptions{}) 58 | if err != nil { 59 | return cleanUpFunc, nil, err 60 | } 61 | 62 | inspRes, err := c.ContainerInspect(ctx, containerID) 63 | if err != nil { 64 | return cleanUpFunc, nil, err 65 | } 66 | hostPort := inspRes.NetworkSettings.Ports[consts.RedisContainerPort][0] 67 | 68 | return cleanUpFunc, redis.NewClient(&redis.Options{ 69 | Addr: fmt.Sprintf("%s:%s", consts.RedisContainerIP, hostPort.HostPort), 70 | DB: db, 71 | }), nil 72 | } 73 | -------------------------------------------------------------------------------- /server/shared/tools/ip.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | ) 7 | 8 | func GetLocalIPv4Address() (string, error) { 9 | addr, err := net.InterfaceAddrs() 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | for _, addr := range addr { 15 | ipNet, isIpNet := addr.(*net.IPNet) 16 | if isIpNet && !ipNet.IP.IsLoopback() { 17 | ipv4 := ipNet.IP.To4() 18 | if ipv4 != nil { 19 | return ipv4.String(), nil 20 | } 21 | } 22 | } 23 | return "", errors.New("not found ipv4 address") 24 | } 25 | -------------------------------------------------------------------------------- /server/shared/tools/port.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/consts" 7 | ) 8 | 9 | // GetFreePort get a free port. 10 | func GetFreePort() (int, error) { 11 | addr, err := net.ResolveTCPAddr(consts.TCP, consts.FreePortAddress) 12 | if err != nil { 13 | return 0, err 14 | } 15 | 16 | l, err := net.ListenTCP(consts.TCP, addr) 17 | if err != nil { 18 | return 0, err 19 | } 20 | 21 | defer l.Close() 22 | return l.Addr().(*net.TCPAddr).Port, nil 23 | } 24 | -------------------------------------------------------------------------------- /server/shared/tools/resp.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/CyanAsterisk/FreeCar/server/shared/errno" 7 | "github.com/CyanAsterisk/FreeCar/server/shared/kitex_gen/base" 8 | ) 9 | 10 | func BuildBaseResp(err error) *base.BaseResponse { 11 | if err == nil { 12 | return baseResp(errno.Success) 13 | } 14 | e := errno.ErrNo{} 15 | if errors.As(err, &e) { 16 | return baseResp(e) 17 | } 18 | s := errno.ServiceErr.WithMessage(err.Error()) 19 | return baseResp(s) 20 | } 21 | 22 | func baseResp(err errno.ErrNo) *base.BaseResponse { 23 | return &base.BaseResponse{ 24 | StatusCode: err.ErrCode, 25 | StatusMsg: err.ErrMsg, 26 | } 27 | } 28 | 29 | func ParseBaseResp(resp *base.BaseResponse) error { 30 | if resp.StatusCode == errno.Success.ErrCode { 31 | return nil 32 | } 33 | return errno.NewErrNo(resp.StatusCode, resp.StatusMsg) 34 | } 35 | --------------------------------------------------------------------------------