├── .dockerignore
├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ └── go.yml
├── .gitignore
├── .golangci.yml
├── .vscode
├── .gitignore
├── launch.json
├── sort_launch.js
└── tasks.json
├── CHANGELOG.md
├── Dockerfile
├── Dockerfile.allinone
├── INSTALL.md
├── LICENSE
├── Makefile
├── README.md
├── ROADMAP.md
├── auto
├── api
│ ├── m
│ │ └── v1
│ │ │ └── user.go
│ ├── r
│ │ └── v1
│ │ │ └── user.go
│ ├── s
│ │ └── v1
│ │ │ └── user.go
│ ├── v1
│ │ ├── admin.go
│ │ ├── alipay_priv.go
│ │ ├── alipay_pub.go
│ │ ├── core.go
│ │ ├── followship.go
│ │ ├── friendship.go
│ │ ├── loose.go
│ │ ├── priv.go
│ │ ├── pub.go
│ │ ├── relax.go
│ │ ├── site.go
│ │ └── trends.go
│ └── x
│ │ └── v1
│ │ └── user.go
└── rpc
│ ├── core
│ └── v1
│ │ ├── auth.pb.go
│ │ └── auth_grpc.pb.go
│ └── greet
│ └── v1
│ ├── greet.pb.go
│ └── greet_grpc.pb.go
├── buf.gen.yaml
├── buf.work.yaml
├── build-allinone.sh
├── build-image.sh
├── build-release.sh
├── cmd
├── migrate
│ └── migrate.go
├── root.go
├── serve
│ └── serve.go
└── version.go
├── config.yaml.sample
├── default.pgo
├── deployed-sites.md
├── docker-compose.yaml
├── docker
├── README.md
├── build
│ └── finalize.sh
├── config.yaml
├── s6
│ ├── .s6-svscan
│ │ └── finish
│ ├── meilisearch
│ │ ├── run
│ │ └── setup
│ ├── paopao-ce
│ │ ├── run
│ │ └── setup
│ └── redis
│ │ └── run
└── start.sh
├── docs
├── README.md
├── deploy
│ ├── README.md
│ ├── aliyun
│ │ └── README.md
│ ├── core
│ │ ├── 001-配置文件说明.md
│ │ └── README.md
│ ├── huawei
│ │ └── README.md
│ ├── k8s
│ │ └── README.md
│ ├── local
│ │ ├── 001-本地开发依赖环境部署.md
│ │ ├── README.md
│ │ └── custom
│ │ │ └── config.yaml
│ └── tencent
│ │ └── README.md
├── discuss
│ ├── 0000-讨论样式模版.md
│ ├── 0001-FAQs.md
│ └── README.md
├── openapi
│ ├── assets
│ │ └── rapidoc-min.js
│ ├── embed.go
│ ├── index.html
│ └── openapi.json
└── proposal
│ ├── .assets
│ ├── 000-00.jpg
│ ├── 000-00.png
│ ├── 000-01.png
│ ├── 001-01.drawio
│ ├── 001-01.png
│ ├── 001-02.drawio
│ ├── 001-02.png
│ ├── 001-03.drawio
│ ├── 001-03.png
│ ├── 002-01.png
│ ├── 002-02.png
│ ├── 002-07.png
│ ├── 002-08.png
│ ├── 006-01.drawio
│ ├── 006-01.png
│ └── README.md
│ ├── .proposal.md
│ ├── 22110409-关于Followship功能项的设计.md
│ ├── 22110410-关于Friendship功能项的设计.md
│ ├── 22110411-关于paopao-ce的设计定位.md
│ ├── 22112109-引入go-mir优化后端架构设计.md
│ ├── 22112309-关于paopao-ce的结构设计.md
│ ├── 22121409-关于Lightship功能项的设计.md
│ ├── 23011309-优化paopao-ce前后端国际化i18n的设计.md
│ ├── 23011310-优化前端运行时配置获取机制的设计.md
│ ├── 23020910-关于paopao-ce引入sqlx与sqlc作为数据逻辑层ORM的构想 copy.md
│ ├── 23021310-关于paopao-ce引入bcrypt作为用户密码加密算法的设计.md
│ ├── 23021510-关于使用pyroscope用于性能调试的设计.md
│ ├── 23040412-关于使用sentry用于错误追踪与性能检测的设计.md
│ ├── 23062120-关于多媒体资源URI信息存储优化.md
│ ├── 23062121-关于Migration的优化.md
│ ├── 23062905-添加Pprof功能特性用于获取Profile.md
│ ├── 23062906-关于开启pgo编译优化.md
│ ├── README.md
│ └── 提案模板.md
├── features-status.md
├── go.mod
├── go.sum
├── internal
├── conf
│ ├── alipay.go
│ ├── cache.go
│ ├── cache_redis.go
│ ├── conf.go
│ ├── config.yaml
│ ├── db.go
│ ├── db_cgo.go
│ ├── db_gorm.go
│ ├── db_nocgo.go
│ ├── logger.go
│ ├── logger_meili.go
│ ├── logger_observe.go
│ ├── logger_zinc.go
│ ├── sentry.go
│ ├── setting.go
│ └── telemetry.go
├── core
│ ├── authority.go
│ ├── cache.go
│ ├── comments.go
│ ├── core.go
│ ├── cs
│ │ ├── comments.go
│ │ ├── contact.go
│ │ ├── cs.go
│ │ ├── errors.go
│ │ ├── messages.go
│ │ ├── metrics.go
│ │ ├── search.go
│ │ ├── timeline.go
│ │ ├── topics.go
│ │ ├── trading.go
│ │ ├── trends.go
│ │ ├── tweets.go
│ │ └── user.go
│ ├── messages.go
│ ├── metrics.go
│ ├── ms
│ │ ├── authority.go
│ │ ├── comments.go
│ │ ├── messages.go
│ │ ├── ms.go
│ │ ├── security.go
│ │ ├── timeline.go
│ │ ├── tweets.go
│ │ ├── user.go
│ │ └── wallet.go
│ ├── search.go
│ ├── security.go
│ ├── storage.go
│ ├── timeline.go
│ ├── topics.go
│ ├── trends.go
│ ├── tweets.go
│ ├── user.go
│ ├── version.go
│ └── wallet.go
├── dao
│ ├── cache
│ │ ├── base.go
│ │ ├── bigcache.go
│ │ ├── cache.go
│ │ ├── common.go
│ │ ├── events.go
│ │ ├── none.go
│ │ ├── redis.go
│ │ ├── simple.go
│ │ ├── tweets.go
│ │ └── web.go
│ ├── dao.go
│ ├── jinzhu
│ │ ├── authority.go
│ │ ├── comments.go
│ │ ├── contacts.go
│ │ ├── dbr
│ │ │ ├── attachment.go
│ │ │ ├── captcha.go
│ │ │ ├── comment.go
│ │ │ ├── comment_content.go
│ │ │ ├── comment_reply.go
│ │ │ ├── comment_thumbs.go
│ │ │ ├── contact.go
│ │ │ ├── following.go
│ │ │ ├── message.go
│ │ │ ├── metrics.go
│ │ │ ├── model.go
│ │ │ ├── post.go
│ │ │ ├── post_attachment_bill.go
│ │ │ ├── post_collection.go
│ │ │ ├── post_content.go
│ │ │ ├── post_star.go
│ │ │ ├── topic.go
│ │ │ ├── user.go
│ │ │ ├── wallet_recharge.go
│ │ │ └── wallet_statement.go
│ │ ├── following.go
│ │ ├── gorm.go
│ │ ├── jinzhu.go
│ │ ├── messages.go
│ │ ├── metrics.go
│ │ ├── security.go
│ │ ├── timeline.go
│ │ ├── topics.go
│ │ ├── trends.go
│ │ ├── tweets.go
│ │ ├── user.go
│ │ ├── utils.go
│ │ └── wallet.go
│ ├── sakila
│ │ └── sakila.go
│ ├── search
│ │ ├── bridge.go
│ │ ├── filter.go
│ │ ├── meili.go
│ │ ├── search.go
│ │ └── zinc.go
│ ├── security
│ │ ├── attachment_check.go
│ │ ├── phone_verify_juhe.go
│ │ └── security.go
│ ├── slonik
│ │ └── slonik.go
│ └── storage
│ │ ├── alioss.go
│ │ ├── cos.go
│ │ ├── huaweiobs.go
│ │ ├── localoss.go
│ │ ├── minio.go
│ │ └── storage.go
├── infra
│ ├── events
│ │ ├── events.go
│ │ ├── events_tryst.go
│ │ └── jobs.go
│ ├── infra.go
│ ├── metrics
│ │ ├── metrics.go
│ │ ├── metrics_tryst.go
│ │ ├── prometheus
│ │ │ ├── metrics.go
│ │ │ └── prometheus.go
│ │ └── statistics
│ │ │ └── cache.go
│ └── migration
│ │ ├── migration.go
│ │ └── migration_embed.go
├── internal.go
├── model
│ ├── enum
│ │ ├── builtin.go
│ │ ├── builtin_enum.go
│ │ └── enum.go
│ ├── joint
│ │ ├── joint.go
│ │ ├── json.go
│ │ └── page.go
│ ├── model.go
│ └── web
│ │ ├── admin.go
│ │ ├── alipay.go
│ │ ├── audit.go
│ │ ├── core.go
│ │ ├── followship.go
│ │ ├── friendship.go
│ │ ├── loose.go
│ │ ├── priv.go
│ │ ├── pub.go
│ │ ├── relax.go
│ │ ├── site.go
│ │ ├── trends.go
│ │ ├── utils.go
│ │ ├── web.go
│ │ └── xerror.go
├── servants
│ ├── admin
│ │ ├── admin.go
│ │ └── user.go
│ ├── base
│ │ ├── base.go
│ │ ├── events.go
│ │ └── page.go
│ ├── bot
│ │ ├── bot.go
│ │ └── user.go
│ ├── chain
│ │ ├── admin.go
│ │ ├── audit.go
│ │ ├── chain.go
│ │ ├── events.go
│ │ ├── jwt.go
│ │ ├── measure.go
│ │ ├── metrics.go
│ │ ├── priv.go
│ │ └── xerror.go
│ ├── docs
│ │ ├── docs.go
│ │ └── docs_embed.go
│ ├── localoss
│ │ ├── localoss.go
│ │ └── user.go
│ ├── mobile
│ │ ├── greet.go
│ │ └── mobile.go
│ ├── servants.go
│ ├── space
│ │ ├── space.go
│ │ └── user.go
│ ├── statick
│ │ ├── statick.go
│ │ └── statick_embed.go
│ └── web
│ │ ├── admin.go
│ │ ├── alipay.go
│ │ ├── assets
│ │ ├── assets.go
│ │ └── comic.ttf
│ │ ├── core.go
│ │ ├── events.go
│ │ ├── followship.go
│ │ ├── friendship.go
│ │ ├── jobs.go
│ │ ├── loose.go
│ │ ├── priv.go
│ │ ├── pub.go
│ │ ├── relax.go
│ │ ├── site.go
│ │ ├── trends.go
│ │ ├── utils.go
│ │ └── web.go
└── service
│ ├── admin.go
│ ├── bot.go
│ ├── docs.go
│ ├── frontend_web.go
│ ├── grpc_server.go
│ ├── grpc_service.go
│ ├── http_server.go
│ ├── http_service.go
│ ├── localoss.go
│ ├── metrics.go
│ ├── mobile.go
│ ├── pprof.go
│ ├── server.go
│ ├── service.go
│ ├── space.go
│ └── web.go
├── magefile.go
├── main.go
├── mirc
├── README.md
├── admin
│ ├── README.md
│ └── v1
│ │ └── user.go
├── bot
│ ├── README.md
│ └── v1
│ │ └── user.go
├── gen.go
├── localoss
│ ├── README.md
│ └── v1
│ │ └── user.go
├── space
│ ├── README.md
│ └── v1
│ │ └── user.go
└── web
│ ├── README.md
│ └── v1
│ ├── admin.go
│ ├── alipay.go
│ ├── core.go
│ ├── followship.go
│ ├── friendship.go
│ ├── loose.go
│ ├── priv.go
│ ├── pub.go
│ ├── relax.go
│ ├── site.go
│ └── trends.go
├── pkg
├── app
│ ├── app.go
│ ├── form.go
│ ├── jwt.go
│ └── pagination.go
├── convert
│ ├── convert.go
│ ├── convert_suite_test.go
│ └── convert_test.go
├── debug
│ ├── annotation.go
│ ├── errors.go
│ ├── pprof_embed.go
│ ├── pyroscope.go
│ └── pyroscope_embed.go
├── http
│ ├── client.go
│ ├── http.go
│ ├── http_suite_test.go
│ ├── mux.go
│ └── mux_test.go
├── json
│ ├── go_json.go
│ ├── json.go
│ ├── jsoniter.go
│ └── sonic.go
├── naming
│ ├── naming.go
│ ├── naming_test.go
│ ├── simple_ns.go
│ └── snake_ns.go
├── obx
│ └── obx.go
├── types
│ ├── bitmap_roaring.go
│ ├── bitmap_roaring_test.go
│ ├── io.go
│ ├── json_box.go
│ ├── json_box_test.go
│ ├── password.go
│ ├── types.go
│ └── types_suite_test.go
├── utils
│ ├── banner.go
│ ├── ip.go
│ ├── ip_test.go
│ ├── iploc
│ │ ├── iploc.go
│ │ ├── iploc_suite_test.go
│ │ ├── iploc_test.go
│ │ └── qqwry.dat
│ ├── md5.go
│ ├── md5_test.go
│ ├── str.go
│ └── utils_suite_test.go
├── version
│ ├── build.go
│ └── version.go
├── xerror
│ ├── common.go
│ └── xerror.go
└── zinc
│ └── zinc.go
├── proto
├── RADME.md
├── buf.yaml
├── core
│ └── v1
│ │ └── auth.proto
└── greet
│ └── v1
│ └── greet.proto
├── scripts
├── README
├── docker
│ ├── Dockerfile.allinone-runner
│ ├── Dockerfile.backend-builder
│ ├── Dockerfile.backend-runner
│ └── README.md
├── launchd
│ └── info.paopao.web.plist
├── migration
│ ├── embed.go
│ ├── mysql
│ │ ├── 0001_initialize_schema.down.sql
│ │ ├── 0001_initialize_schema.up.sql
│ │ ├── 0002_post_visibility.down.sql
│ │ ├── 0002_post_visibility.up.sql
│ │ ├── 0003_feature_contact.down.sql
│ │ ├── 0003_feature_contact.up.sql
│ │ ├── 0004_optimize_idx.down.sql
│ │ ├── 0004_optimize_idx.up.sql
│ │ ├── 0005_share_count.down.sql
│ │ ├── 0005_share_count.up.sql
│ │ ├── 0006_topic_follow.down.sql
│ │ ├── 0006_topic_follow.up.sql
│ │ ├── 0007_comment_thumbs.down.sql
│ │ ├── 0007_comment_thumbs.up.sql
│ │ ├── 0008_content_type_alter.down.sql
│ │ ├── 0008_content_type_alter.up.sql
│ │ ├── 0009_create_view_post_filter.down.sql
│ │ ├── 0009_create_view_post_filter.up.sql
│ │ ├── 0010_user_following.down.sql
│ │ ├── 0010_user_following.up.sql
│ │ ├── 0011_home_timeline.down.sql
│ │ ├── 0011_home_timeline.up.sql
│ │ ├── 0012_comment_essence.down.sql
│ │ ├── 0012_comment_essence.up.sql
│ │ ├── 0013_rank_metrics.down.sql
│ │ ├── 0013_rank_metrics.up.sql
│ │ ├── 0014_user_relation_view.down.sql
│ │ ├── 0014_user_relation_view.up.sql
│ │ ├── 0015_topic_user_pin.down.sql
│ │ └── 0015_topic_user_pin.up.sql
│ ├── postgres
│ │ ├── 0001_initialize_schema.down.sql
│ │ ├── 0001_initialize_schema.up.sql
│ │ ├── 0002_post_visibility.down.sql
│ │ ├── 0002_post_visibility.up.sql
│ │ ├── 0003_feature_contact.down.sql
│ │ ├── 0003_feature_contact.up.sql
│ │ ├── 0004_share_count.down.sql
│ │ ├── 0004_share_count.up.sql
│ │ ├── 0005_topic_follow.down.sql
│ │ ├── 0005_topic_follow.up.sql
│ │ ├── 0006_comment_thumbs.down.sql
│ │ ├── 0006_comment_thumbs.up.sql
│ │ ├── 0007_content_type_alter.down.sql
│ │ ├── 0007_content_type_alter.up.sql
│ │ ├── 0008_create_view_post_filter.down.sql
│ │ ├── 0008_create_view_post_filter.up.sql
│ │ ├── 0009_user_following.down.sql
│ │ ├── 0009_user_following.up.sql
│ │ ├── 0010_home_timeline.down.sql
│ │ ├── 0010_home_timeline.up.sql
│ │ ├── 0011_comment_essence.down.sql
│ │ ├── 0011_comment_essence.up.sql
│ │ ├── 0012_rank_metrics.down.sql
│ │ ├── 0012_rank_metrics.up.sql
│ │ ├── 0013_user_relation_view.down.sql
│ │ ├── 0013_user_relation_view.up.sql
│ │ ├── 0014_topic_user_pin.down.sql
│ │ └── 0014_topic_user_pin.up.sql
│ └── sqlite3
│ │ ├── 0001_initialize_schema.down.sql
│ │ ├── 0001_initialize_schema.up.sql
│ │ ├── 0002_post_visibility.down.sql
│ │ ├── 0002_post_visibility.up.sql
│ │ ├── 0003_feature_contact.down.sql
│ │ ├── 0003_feature_contact.up.sql
│ │ ├── 0004_optimize_idx.down.sql
│ │ ├── 0004_optimize_idx.up.sql
│ │ ├── 0005_share_count.down.sql
│ │ ├── 0005_share_count.up.sql
│ │ ├── 0006_topic_follow.down.sql
│ │ ├── 0006_topic_follow.up.sql
│ │ ├── 0007_comment_thumbs.down.sql
│ │ ├── 0007_comment_thumbs.up.sql
│ │ ├── 0008_content_type_alter.down.sql
│ │ ├── 0008_content_type_alter.up.sql
│ │ ├── 0009_create_view_post_filter.down.sql
│ │ ├── 0009_create_view_post_filter.up.sql
│ │ ├── 0010_user_following.down.sql
│ │ ├── 0010_user_following.up.sql
│ │ ├── 0011_home_timeline.down.sql
│ │ ├── 0011_home_timeline.up.sql
│ │ ├── 0012_comment_essence.down.sql
│ │ ├── 0012_comment_essence.up.sql
│ │ ├── 0013_rank_metrics.down.sql
│ │ ├── 0013_rank_metrics.up.sql
│ │ ├── 0014_user_relation_view.down.sql
│ │ ├── 0014_user_relation_view.up.sql
│ │ ├── 0015_topic_user_pin.down.sql
│ │ └── 0015_topic_user_pin.up.sql
├── paopao-mysql.sql
├── paopao-postgres.sql
├── paopao-sqlite3.sql
└── systemd
│ └── paopao.service
└── web
├── .dockerignore
├── .env
├── .gitignore
├── Dockerfile
├── README.md
├── biome.json
├── dist
├── assets
│ ├── 404-BFAjwVrS.css
│ ├── 404-Bdi9VVB7.js
│ ├── @css-render-DN2R7sM6.js
│ ├── @emotion-WldOFDRm.js
│ ├── @juggle-C8OzoCMD.js
│ ├── @opentiny-BDqeqNoT.css
│ ├── @opentiny-CL_kZccX.js
│ ├── @vicons-PCg97L0F.js
│ ├── @vue-9sINKCPW.js
│ ├── Anouncement-Bku_tnhO.js
│ ├── Anouncement-DBcpQaHQ.css
│ ├── Collection-BxnFVUdJ.js
│ ├── Collection-CjoOEYyd.css
│ ├── Contacts-BHvHu1HY.css
│ ├── Contacts-BL8KP4H1.js
│ ├── FiraCode-Regular-CRwVj4V2.woff2
│ ├── Following-BWx13w_x.js
│ ├── Following-XPfMtEVy.css
│ ├── Home-6WuUxluX.css
│ ├── Home-BUYa9lNB.js
│ ├── IEnum-B3rDUvtK.js
│ ├── LatoLatin-Regular-Dmlz1U0B.woff2
│ ├── LatoLatin-Semibold-Dbk81p2D.woff2
│ ├── Messages-ByQEfKKw.css
│ ├── Messages-CCLY8J57.js
│ ├── Post-B5Cq4lWE.js
│ ├── Post-CppCIvfc.css
│ ├── Profile-DZnwpobO.js
│ ├── Profile-DdmCVl1_.css
│ ├── Setting-B6FP3CgO.js
│ ├── Setting-F1hZZqDf.css
│ ├── Topic-DO-EgRWc.js
│ ├── Topic-Dk7qWh97.css
│ ├── User-BVeHsW_X.js
│ ├── User-DlRmS904.css
│ ├── Wallet-D2b31y6W.css
│ ├── Wallet-mSEGcp1e.js
│ ├── async-validator-9PlIezaS.js
│ ├── axios-t--hEgTQ.js
│ ├── content-BEAgLL5B.css
│ ├── content-x8CCof-x.js
│ ├── copy-to-clipboard-DgsYVcxl.js
│ ├── count-BK58UQ2M.js
│ ├── css-render-BDrvWz3H.js
│ ├── cssfilter-l0sNRNKZ.js
│ ├── date-fns-Db9XENWt.js
│ ├── date-fns-tz-l0sNRNKZ.js
│ ├── dijkstrajs-C00ieaqj.js
│ ├── discover-tweets-DGidPW73.jpeg
│ ├── errortips-bg-DB72-mLU.png
│ ├── evtd-CI_DDEu_.js
│ ├── following-tweets-BKofJ8VU.jpeg
│ ├── index-BeZzJvVk.css
│ ├── index-DxHQoSDp.js
│ ├── lodash-CGvuAYz8.js
│ ├── lodash-es-TJvrUncL.js
│ ├── logo-wT_OfKx5.png
│ ├── main-nav-DkRZ0XqZ.css
│ ├── main-nav.vue_vue_type_style_index_0_lang-D7-FAbTY.js
│ ├── moment-P60zs0je.js
│ ├── naive-ui-BJojRuLw.js
│ ├── paopao-video-player-FrzfkELx.js
│ ├── post-item-CiouHqhK.css
│ ├── post-item.vue_vue_type_style_index_0_lang-3gNUBBWd.js
│ ├── post-skeleton-B6KFVL2X.js
│ ├── post-skeleton-DtiTm5JG.css
│ ├── qrcode-CFCLo2rZ.js
│ ├── seemly-D-teBmey.js
│ ├── toggle-selection-DGa8lynz.js
│ ├── treemate-HRdUPn5m.js
│ ├── v3-infinite-loading-C33VokCe.css
│ ├── v3-infinite-loading-D3303HHP.js
│ ├── vdirs-DRH9Xvnd.js
│ ├── vfonts-Bnl8eXTc.css
│ ├── vooks-CfQnrjIt.js
│ ├── vue-CXjY62Zb.js
│ ├── vue-router-yrkFRUM9.js
│ ├── vueuc-DrMWnH2h.js
│ ├── vuex--ttreJMD.js
│ ├── whisper-CD0RDnNY.js
│ ├── whisper-CxECGYE1.css
│ ├── whisper-add-friend-C7nzJlGG.css
│ ├── whisper-add-friend-EUQIGn8g.js
│ └── xss-l0sNRNKZ.js
├── favicon.ico
├── index.html
├── logo.png
├── manifest.json
└── sw.js
├── embed.go
├── index.html
├── package.json
├── public
├── favicon.ico
├── logo.png
├── manifest.json
└── sw.js
├── src-tauri
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── build.rs
├── icons
│ ├── 128x128.png
│ ├── 128x128@2x.png
│ ├── 32x32.png
│ ├── icon.icns
│ └── icon.ico
├── src
│ └── main.rs
└── tauri.conf.json
├── src
├── App.vue
├── api
│ ├── auth.ts
│ ├── post.ts
│ ├── site.ts
│ └── user.ts
├── assets
│ ├── css
│ │ └── main.less
│ ├── img
│ │ ├── discover-tweets.jpeg
│ │ ├── following-tweets.jpeg
│ │ ├── fresh-tweets.png
│ │ └── logo.png
│ └── logo.png
├── components
│ ├── auth.vue
│ ├── comment-item.vue
│ ├── compose-comment.vue
│ ├── compose-reply.vue
│ ├── compose.vue
│ ├── contact-item.vue
│ ├── follow-item.vue
│ ├── main-nav.vue
│ ├── message-item.vue
│ ├── message-skeleton.vue
│ ├── mobile-post-item.vue
│ ├── post-attachment.vue
│ ├── post-detail.vue
│ ├── post-image.vue
│ ├── post-item.vue
│ ├── post-link.vue
│ ├── post-skeleton.vue
│ ├── post-video.vue
│ ├── reply-item.vue
│ ├── rightbar.vue
│ ├── sidebar.vue
│ ├── tag-item.vue
│ ├── whisper-add-friend.vue
│ └── whisper.vue
├── main.ts
├── router
│ └── index.ts
├── shims-vue.d.ts
├── store
│ └── index.ts
├── types
│ ├── Core.d.ts
│ ├── Item.d.ts
│ ├── NetParams.d.ts
│ └── NetReq.d.ts
├── utils
│ ├── IEnum.ts
│ ├── content.ts
│ ├── count.ts
│ ├── formatTime.ts
│ ├── isZipFile.ts
│ ├── request.ts
│ └── scrollToTop.ts
├── views
│ ├── 404.vue
│ ├── Anouncement.vue
│ ├── Collection.vue
│ ├── Contacts.vue
│ ├── Following.vue
│ ├── Home.vue
│ ├── Messages.vue
│ ├── Post.vue
│ ├── Profile.vue
│ ├── Setting.vue
│ ├── Topic.vue
│ ├── User.vue
│ └── Wallet.vue
├── vite-env.d.ts
└── vuex.d.ts
├── tsconfig.json
└── vite.config.ts
/.dockerignore:
--------------------------------------------------------------------------------
1 | data
2 | .vscode
3 | release
4 | custom
5 | *Dockerfile*
6 | web/node_modules
7 | web/src-tauri/target
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ts linguist-language=go
2 | *.js linguist-language=go
3 | *.css linguist-language=go
4 | *.vue linguist-language=go
5 | *.html linguist-language=go
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "gomod"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 | target-branch: "dev"
13 | reviewers:
14 | - "rocboss"
15 | - "alimy"
16 | commit-message:
17 | prefix: "mod:"
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .mirc
3 | .idea
4 | __debug_bin
5 | !*.example
6 | /config.yaml
7 | *.log
8 | /paopao-ce
9 | /paopao
10 | /.env
11 | /.envrc
12 | /release
13 | /data
14 | /custom
15 | /.custom
16 | /run.sh
17 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | linters-settings:
2 | staticcheck:
3 | checks: [
4 | "all",
5 | "-SA1019" # There are valid use cases of strings.Title
6 | ]
7 | nakedret:
8 | max-func-lines: 0 # Disallow any unnamed return statement
9 |
10 | linters:
11 | enable:
12 | # - unused
13 | # - errcheck
14 | # - gosimple
15 | - govet
16 | # - ineffassign
17 | # - staticcheck
18 | # - typecheck
19 | # - nakedret
20 | - gofmt
21 | # - rowserrcheck
22 | # - unconvert
23 | - goimports
24 | # - unparam
25 |
--------------------------------------------------------------------------------
/.vscode/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | *.log
3 | __debug_bin
4 | settings.json
5 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "paopao-ce [debug]",
6 | "type": "go",
7 | "request": "launch",
8 | "mode": "exec",
9 | "program": "${workspaceFolder}/.vscode/__debug_bin",
10 | "args":[
11 | "serve"
12 | ],
13 | "preLaunchTask": "go: build (debug)",
14 | "cwd": "${workspaceFolder}"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/.vscode/sort_launch.js:
--------------------------------------------------------------------------------
1 | let launch = require('./launch.json');
2 | launch.configurations.sort((a, b) => a.name.localeCompare(b.name));
3 | let fs = require('fs');
4 | fs.writeFileSync('launch.json', JSON.stringify(launch, null, 4));
5 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "go: build (debug)",
6 | "type": "shell",
7 | "command": "go",
8 | "args": [
9 | "build",
10 | "-gcflags=all=-N -l",
11 | "-tags",
12 | "'embed go_json'",
13 | "-o",
14 | "${workspaceFolder}/.vscode/__debug_bin"
15 | ],
16 | "options": {
17 | "cwd": "${workspaceFolder}"
18 | }
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 ROC
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/buf.gen.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | managed:
3 | enabled: true
4 | go_package_prefix:
5 | default: github.com/rocboss/paopao-ce/auto/rpc
6 | except:
7 | - buf.build/googleapis/googleapis
8 | plugins:
9 | - plugin: go
10 | out: auto/rpc
11 | opt: paths=source_relative
12 | - plugin: go-grpc
13 | out: auto/rpc
14 | opt:
15 | - paths=source_relative
16 | - require_unimplemented_servers=true
17 |
--------------------------------------------------------------------------------
/buf.work.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | directories:
3 | - proto
4 |
--------------------------------------------------------------------------------
/build-allinone.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # eg.1 : sh build-image.sh
3 | # eg.2, set image: sh build-image.sh bitbus/paopao-ce
4 |
5 | VERSION=`git describe --tags --always | cut -f1,2 -d "-"` # eg.: 0.2.5
6 | IMAGE="bitbus/paopao-ce"
7 |
8 | if [ -n "$1" ]; then
9 | IMAGE="$1"
10 | fi
11 | if [ -n "$2" ]; then
12 | VERSION="$2"
13 | fi
14 |
15 | # build image
16 | docker buildx build \
17 | --build-arg USE_DIST="yes" \
18 | --tag "$IMAGE:all-in-one-${VERSION}" \
19 | --tag "$IMAGE:all-in-one-latest" \
20 | . -f Dockerfile.allinone
21 |
22 | # push to image rep
23 | # if [ -n "$1" ]; then
24 | # docker push "$IMAGE:${VERSION}"
25 | # docker push "$IMAGE:latest"
26 | # fi
27 |
--------------------------------------------------------------------------------
/build-image.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # eg.1 : sh build-image.sh
3 | # eg.2, set image: sh build-image.sh bitbus/paopao-ce
4 |
5 | VERSION=`git describe --tags --always | cut -f1,2 -d "-"` # eg.: 0.2.5
6 | IMAGE="bitbus/paopao-ce"
7 |
8 | if [ -n "$1" ]; then
9 | IMAGE="$1"
10 | fi
11 | if [ -n "$2" ]; then
12 | VERSION="$2"
13 | fi
14 |
15 | # build image
16 | docker buildx build \
17 | --build-arg USE_DIST="yes" \
18 | --tag "$IMAGE:${VERSION}" \
19 | --tag "$IMAGE:latest" \
20 | . -f Dockerfile
21 |
22 | # push to image rep
23 | # if [ -n "$1" ]; then
24 | # docker push "$IMAGE:${VERSION}"
25 | # docker push "$IMAGE:latest"
26 | # fi
27 |
--------------------------------------------------------------------------------
/build-release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # eg.1 : sh build-image.sh
3 | # eg.2, set tags: sh build-image.sh 'go_json'
4 |
5 | TAGS='go_json'
6 |
7 | if [ -n "$1" ]; then
8 | TAGS="$1"
9 | fi
10 |
11 | make release CGO_ENABLED=0 TAGS="$TAGS"
12 |
--------------------------------------------------------------------------------
/cmd/migrate/migrate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package migrate
6 |
7 | import (
8 | "fmt"
9 |
10 | "github.com/rocboss/paopao-ce/cmd"
11 | "github.com/spf13/cobra"
12 | )
13 |
14 | func init() {
15 | migrateCmd := &cobra.Command{
16 | Use: "migrate",
17 | Short: "migrate database data",
18 | Long: "miegrate database data when paopao-ce upgrade",
19 | Run: migrateRun,
20 | }
21 | cmd.Register(migrateCmd)
22 | }
23 |
24 | func migrateRun(_cmd *cobra.Command, _args []string) {
25 | // TODO: add some logic for migrate cmd feature
26 | fmt.Println("sorry, this feature is not implemented yet.")
27 | }
28 |
--------------------------------------------------------------------------------
/cmd/root.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cmd
6 |
7 | import (
8 | "github.com/spf13/cobra"
9 | )
10 |
11 | var (
12 | rootCmd = &cobra.Command{
13 | Use: "paopao",
14 | Short: `an artistic "twitter like" community`,
15 | Long: `an artistic "twitter like" community`,
16 | }
17 | )
18 |
19 | // Setup set root command name,short-describe, long-describe
20 | // return &cobra.Command to custom other options
21 | func Setup(use, short, long string) *cobra.Command {
22 | rootCmd.Use = use
23 | rootCmd.Short = short
24 | rootCmd.Long = long
25 | return rootCmd
26 | }
27 |
28 | // Register add sub-command
29 | func Register(cmd *cobra.Command) {
30 | rootCmd.AddCommand(cmd)
31 | }
32 |
33 | // Execute start application
34 | func Execute() {
35 | rootCmd.Execute()
36 | }
37 |
--------------------------------------------------------------------------------
/cmd/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cmd
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/pkg/utils"
9 | "github.com/rocboss/paopao-ce/pkg/version"
10 | "github.com/spf13/cobra"
11 | )
12 |
13 | func init() {
14 | versionCmd := &cobra.Command{
15 | Use: "version",
16 | Short: "show version information",
17 | Long: "show version information",
18 | Run: versionRun,
19 | }
20 | Register(versionCmd)
21 | }
22 |
23 | func versionRun(_cmd *cobra.Command, _args []string) {
24 | utils.PrintHelloBanner(version.VersionInfo())
25 | }
26 |
--------------------------------------------------------------------------------
/default.pgo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/default.pgo
--------------------------------------------------------------------------------
/deployed-sites.md:
--------------------------------------------------------------------------------
1 | ## 部署站点
2 | PaoPao部署站点信息。
3 |
4 | | 名称 | 网址 | 站长 | 备注 |
5 | | ----- | ----- | ----- | ----- |
6 | |泡泡|[www.paopao.info](https://www.paopao.info)|[ROC](https://www.paopao.info/#/user?username=roc 'roc(@paopao.info)')|PaoPao官方站点|
7 | |布里塔|[bulita.cn](https://bulita.cn)|[chendong](https://www.paopao.info/#/user?username=chendong 'chendong(@paopao.info)')|招聘求职等信息|
8 | |提示词社区|[promptser.cn](https://promptser.cn)|[henryspace](https://paopao.info/#/user?username=henryspace 'henryspace(@paopao.info)')|提示词社区|
9 | |iiBiuBiu|[iibiubiu.com](https://iibiubiu.com)|[北野](https://www.paopao.info/#/user?username=alimy 'alimy(@paopao.info)')|开发、测试备用机,可以体验最新版本paopao-ce|
10 |
--------------------------------------------------------------------------------
/docker/README.md:
--------------------------------------------------------------------------------
1 | # Docker for paopao-ce
2 |
3 | TODO;
--------------------------------------------------------------------------------
/docker/build/finalize.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -xe
4 |
5 | # Create paopao user for paopao-ce
6 | addgroup -S paopao
7 | adduser -G paopao -H -D -g 'paopao User' paopao -h /app -s /bin/sh && usermod -p '*' paopao && passwd -u paopao
8 | # echo "export PAOPAO_CUSTOM=${PAOPAO_CUSTOM}" >> /etc/profile
9 |
10 | # Final cleaning
11 | mv /app/docker/config.yaml /app/config.yaml
12 | rm -rf /app/docker/build
13 | rm /app/docker/README.md
14 |
--------------------------------------------------------------------------------
/docker/s6/.s6-svscan/finish:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # do nothing now
4 |
--------------------------------------------------------------------------------
/docker/s6/meilisearch/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if test -f ./setup; then
4 | # shellcheck disable=SC2039,SC1091,SC3046
5 | source ./setup
6 | fi
7 |
8 | exec gosu ${USER} /bin/meilisearch
9 |
--------------------------------------------------------------------------------
/docker/s6/meilisearch/setup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd /app/meili_data || exit 1
4 |
--------------------------------------------------------------------------------
/docker/s6/paopao-ce/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if test -f ./setup; then
4 | # shellcheck disable=SC2039,SC1091,SC3046
5 | source ./setup
6 | fi
7 |
8 | exec gosu ${USER} /app/paopao serve
9 |
--------------------------------------------------------------------------------
/docker/s6/paopao-ce/setup:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # sleep 10s等待meilisearch启动完成,这是笨方法,暂时先凑着,后面再找更靠谱的法子;这里之所以要等待meili启动完成,是因为paopao-ce首次启动时需要初始化一次meili的index,如果初始化失败就无法在后续使用。
4 | sleep 10
5 | cd /app || exit 1
6 |
--------------------------------------------------------------------------------
/docker/s6/redis/run:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if test -f ./setup; then
4 | # shellcheck disable=SC2039,SC1091,SC3046
5 | source ./setup
6 | fi
7 |
8 | exec docker-entrypoint.sh redis-server
9 |
--------------------------------------------------------------------------------
/docker/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | create_volume_subfolder() {
4 | # only change ownership if needed, if using an nfs mount this could be expensive
5 | if [ "$USER:$USER" != "$(stat /app -c '%U:%G')" ]
6 | then
7 | # Modify the owner of /app dir, make $USER(paopao) user have permission to create sub-dir in /app.
8 | chown -R "$USER:$USER" /app
9 | fi
10 |
11 | # Create VOLUME subfolder
12 | for f in /app/custom /app/meili_data; do
13 | if ! test -d $f; then
14 | gosu "$USER" mkdir -p $f
15 | fi
16 | done
17 | }
18 |
19 | setids() {
20 | export USER=paopao
21 | PUID=${PUID:-1000}
22 | PGID=${PGID:-1000}
23 | groupmod -o -g "$PGID" $USER
24 | usermod -o -u "$PUID" $USER
25 | }
26 |
27 | setids
28 | create_volume_subfolder
29 |
30 | # Exec CMD or S6 by default if nothing present
31 | if [ $# -gt 0 ];then
32 | exec "$@"
33 | else
34 | exec /bin/s6-svscan /app/gogs/docker/s6/
35 | fi
36 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ## 开发文档
2 | 本目录包含一些开发者文档。
3 |
4 | * [openapi](openapi): api相关文档
5 | * [proposal](proposal): 开发/设计 提按相关文档
6 | * [deploy](deploy): 部署相关文档
7 | * [discuss](discuss): 开发者交流
8 |
--------------------------------------------------------------------------------
/docs/deploy/README.md:
--------------------------------------------------------------------------------
1 | ## 部署文档
2 | 本目录包含一些paopao-ce部署相关的帮助文档
3 |
4 | * [core](./core) - paopao-ce部署帮助文档
5 | * [aliyun](./aliyun) - Aliyun平台部署文档
6 | * [huawei](./huawei) - Huawei Cloud平台部署文档
7 | * [tencnet](./tencent) - Tencent Cloud平台部署文档
8 | * [local](./local) - 本地部署文档
9 | * [k8s](./k8s) - 使用Kubernetes部署paopao-ce相关文档
10 |
--------------------------------------------------------------------------------
/docs/deploy/aliyun/README.md:
--------------------------------------------------------------------------------
1 | ### Aliyun平台部署文档
2 | 本目录包含一些阿里云平台部署paopao-ce的一些帮助文档。
3 |
--------------------------------------------------------------------------------
/docs/deploy/core/README.md:
--------------------------------------------------------------------------------
1 | ### paopao-ce部署帮助文档
2 | 本目录包含一些部署paopao-ce的一些帮助文档。
3 |
4 | * [001-配置文件说明](./001-配置文件说明.md '001-配置文件说明')
5 |
--------------------------------------------------------------------------------
/docs/deploy/huawei/README.md:
--------------------------------------------------------------------------------
1 | ### Huawei Cloud平台部署文档
2 | 本目录包含一些华为云平台部署paopao-ce的一些帮助文档。
3 |
--------------------------------------------------------------------------------
/docs/deploy/k8s/README.md:
--------------------------------------------------------------------------------
1 | ### 使用Kubernetes部署paopao-ce相关文档
2 | 本目录包含一些k8s环境部署paopao-ce的一些帮助文档。
3 |
--------------------------------------------------------------------------------
/docs/deploy/local/README.md:
--------------------------------------------------------------------------------
1 | ### 本地部署文档
2 | 本目录包含一些本地开发环境部署paopao-ce的一些帮助文档。
3 |
4 | * [001-本地开发依赖环境部署](001-本地开发依赖环境部署.md '001-本地开发依赖环境部署')
5 |
--------------------------------------------------------------------------------
/docs/deploy/local/custom/config.yaml:
--------------------------------------------------------------------------------
1 | Features:
2 | Default: ["Sqlite3", "Zinc", "LocalOSS", "LoggerFile", "BigCacheIndex", "Friendship", "Frontend:EmbedWeb", "Web"]
3 | BigCacheIndex: # 使用BigCache缓存泡泡广场消息流
4 | MaxIndexPage: 1024 # 最大缓存页数,必须是2^n, 代表最大同时缓存多少页数据
5 | HardMaxCacheSize: 256 # 最大缓存大小(MB),0表示无限制
6 | WebServer: # Web服务
7 | HttpIp: 0.0.0.0
8 | HttpPort: 8008
9 | ReadTimeout: 60
10 | WriteTimeout: 60
11 | JWT: # 鉴权加密
12 | Secret: 18a6413dc4fe394c66345ebe501b2f26
13 | Issuer: paopao-api
14 | Expire: 86400
15 | Zinc: # Zinc搜索配置
16 | Host: 127.0.0.1:4080
17 | Index: paopao-data
18 | User: admin
19 | Password: admin
20 | Secure: False
21 | LocalOSS: # 本地文件OSS存储配置
22 | SavePath: custom/data/oss
23 | Secure: True
24 | Bucket: paopao
25 | Domain: api.alimy.me
26 | Sqlite3: # Sqlite3数据库
27 | Path: custom/data/sqlite3/paopao-ce.db
28 | Redis:
29 | InitAddress:
30 | - 127.0.0.1:6379
31 |
--------------------------------------------------------------------------------
/docs/deploy/tencent/README.md:
--------------------------------------------------------------------------------
1 | ### Tencent Cloud平台部署文档
2 | 本目录包含一些腾讯云平台部署paopao-ce的一些帮助文档。
3 |
--------------------------------------------------------------------------------
/docs/discuss/0000-讨论样式模版.md:
--------------------------------------------------------------------------------
1 | ### <主题标题>
2 | ====== <这里写主题描述> =====
3 |
4 | #### <话题标题>
5 | ====== <这里写话题描述> =====
6 | * [北野](https://alimy.me) - 2022/12/14 10:07
7 | > 这里就是你的观点论述了
8 |
9 | * [北野](https://alimy.me) - 2022/12/14 10:07
10 | > 这里就是你的观点论述了
11 |
12 | * [北野](https://alimy.me) - 2022/12/14 10:07
13 | > 这里就是你的观点论述了
14 |
15 | * [北野](https://alimy.me) - 2022/12/14 10:07
16 | > 这里就是你的观点论述了
17 |
18 | * [北野](https://alimy.me) - 2022/12/14 10:07
19 | > 这里就是你的回复
20 | >> 这里就是你的观点论述了
21 |
22 | * [北野](https://alimy.me) - 2022/12/14 10:07
23 | > 你的回复
24 | >> 这里就是你的回复
25 | >>> 这里就是你的观点论述了
26 |
27 |
--------------------------------------------------------------------------------
/docs/discuss/0001-FAQs.md:
--------------------------------------------------------------------------------
1 | ### FAQs 常见问题
2 | 一些常见问题的解答。
3 |
4 |
5 | #### 为什么要在代码库[docs](../../docs/)中写开发文档,比如[proposal](../proposal/)提按文档?
6 | * [北野](https://alimy.me) - 2022/12/14 10:20
7 | > 这里有几个原因
8 | > * 开发文档跟随代码库对开发者友好;
9 | > * paopao-ce的开发是离散组织式开发,代码寄存在GitHub,并没有一个统一的开发平台;
10 | > * 为什么没有使用GitHub的Wiki写档?这个后续会把一些文档Copy到WiKi。
11 | > * 文档跟随代码,将更好的与代码共存,而且Markdown足够用于写文档,这才是最核心的,怎么简单怎么来。
12 |
13 |
14 | #### 为什么要在代码库[docs](../../docs/)中包含一个[discuss](../discuss/)?难道没有其他可用工具更好完成这种工作吗,比瑞slack/discord?
15 | * [北野](https://alimy.me) - 2022/12/14 10:32
16 | > discuss目录包含一些关于代码库开发的一些方案讨论,偏静态内容,代码库内置这些方案讨论,也便于git进行跟踪归档。
17 | > 确实有其他一些社交类开发聊天工具可用,比如slack/discord,但是目前paopao-ce的开发者社区还比较小,暂时还没有开通的必要。
18 | * [北野](https://alimy.me) - 2022/12/14 10:50
19 | > 再一个国内访问也不是特别方便,暂时采用目前这种土办法过渡一下,或许后期会选择一个社交平台供开发者交流,比如飞书~
20 |
--------------------------------------------------------------------------------
/docs/discuss/README.md:
--------------------------------------------------------------------------------
1 | ### Discuss 开发者交流
2 | 本目录包含一些开发相关的问题交流论述。
3 |
4 | * [0000-讨论样式模版](./0000-讨论样式模版.md "讨论样式模版")
5 | * [0001-FAQs](./0001-FAQs.md "FAQs")
6 |
7 | ### paopao.info
8 | 关于paopao-ce的交流、反馈,也可以直接在我们官网[paopao.info](https://www.paopao.info)进行,发布动态时记得加上标签`#paopao-ce`或类似主题相关的标签,方便话题查找。欢迎大家在[paopao.info](https://www.paopao.info)畅快愉悦的交流,一起让paopao-ce的用户体验更好、更便捷。
9 |
--------------------------------------------------------------------------------
/docs/openapi/embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build docs
6 | // +build docs
7 |
8 | package openapi
9 |
10 | import (
11 | "embed"
12 | "net/http"
13 | )
14 |
15 | //go:embed index.html openapi.json **/*
16 | var files embed.FS
17 |
18 | // NewFileSystem get an embed static assets http.FileSystem instance.
19 | func NewFileSystem() http.FileSystem {
20 | return http.FS(files)
21 | }
22 |
--------------------------------------------------------------------------------
/docs/openapi/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | paopao-ce develop documents
8 |
9 |
13 |
14 |
15 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/proposal/.assets/000-00.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/000-00.jpg
--------------------------------------------------------------------------------
/docs/proposal/.assets/000-00.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/000-00.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/000-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/000-01.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/001-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/001-01.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/001-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/001-02.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/001-03.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/001-03.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/002-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/002-01.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/002-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/002-02.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/002-07.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/002-07.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/002-08.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/002-08.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/006-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/docs/proposal/.assets/006-01.png
--------------------------------------------------------------------------------
/docs/proposal/.assets/README.md:
--------------------------------------------------------------------------------
1 | ### 图片等资源目录
2 |
--------------------------------------------------------------------------------
/docs/proposal/.proposal.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | YYMMDDHH | 北野 | 2022-11-04 |2022-11-04 | v1.0| 提议 |
4 |
5 | ## <我是标题>
6 | ---- 这里写简要介绍 ----
7 |
8 | ## 场景
9 |
10 | ---- 这里描述在什么使用场景下会需要本提按 ----
11 |
12 | ## 需求
13 |
14 | ---- 这里表述具体需求,在上面描述的场景下,我们需要获得的具体状态 ----
15 |
16 | ## 方案
17 |
18 | ---- 这里提出可能的解决方案 ----
19 |
20 | ## 疑问
21 |
22 | ---- 这里描述上述内容中的疑问,比如方案落地将会遇到的坑,其他方案与本文档的比较 ----
23 |
24 | ## 更新记录
25 | #### v1.0(2022-11-04) - 北野
26 | * 初始文档
--------------------------------------------------------------------------------
/docs/proposal/23011309-优化paopao-ce前后端国际化i18n的设计.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23011309 | 北野 | 2023-01-13 | 2023-01-13 | v0.0 | 提议 |
4 |
5 | ### 概述
6 | 目前paopao-ce前端/后端 都使用中文提供业务服务,暂时还没有提供国际化i18n机制提供国际化的业务服务。本提按提议提过在前端/后端中引入i8n机制,以提供国际化业务服务。 本提按建立在[012-优化前端运行时配置获取机制的设计](012-优化前端运行时配置获取机制的设计.md)基础之上。
7 |
8 | ### 需求
9 | * 提供i18n机制以实现业务服务的国际化 - 前端/后端
10 |
11 | ### 方案
12 | TODO;
13 |
14 | ### 疑问
15 |
16 | 1. 为什么要提供这种机制?
17 | TODO;
18 |
19 | ### 更新记录
20 | #### v0.0(2023-01-13) - 北野
21 | * 初始文档, 先占个位置
22 |
--------------------------------------------------------------------------------
/docs/proposal/23011310-优化前端运行时配置获取机制的设计.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23011310 | 北野 | 2023-01-13 | 2023-01-13 | v0.0 | 提议 |
4 |
5 | ### 概述
6 | 目前的Web前端运行时配置是通过编译时配置[.env](../../web/.env)进行静态配置,虽然能满足简单的功能需求,但是非常不灵活。本提按提议一种由paopao-ce后端服务控制的前端运行时配置获取机制,让前端更灵活的依据运行时配置提供产品服务。
7 |
8 | ### 场景
9 | 前端依据paopao-ce后端服务提供的运行时配置按需提供产品服务,也可以扩展到按用户喜好自定义的配置提供产品服务,比如用户设置的语言、Theme、功能特性等。
10 |
11 | ### 需求
12 | * 前端依据运行时配置提供业务服务 - 前端
13 | * 前端在启动时从paopao-ce后端服务获取运行时配置 - 前端/后端
14 | * 用户可以自定义前端运行时配置,如语言、Theme、功能特性等 - 前端/后端
15 | * 用户自定义的前端运行时配置可以 创建/更新 - 前端/后端
16 | * 后端服务提供默认前端运行时配置 - 后端
17 |
18 | ### 方案
19 | TODO;
20 |
21 | ### 疑问
22 |
23 | 1. 为什么要提供这种机制?
24 | TODO;
25 |
26 | ### 更新记录
27 | #### v0.0(2023-01-13) - 北野
28 | * 初始文档, 先占个位置
29 |
--------------------------------------------------------------------------------
/docs/proposal/23020910-关于paopao-ce引入sqlx与sqlc作为数据逻辑层ORM的构想 copy.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23020910| 北野 | 2023-02-09 | 2023-02-09 | v0.0 | 提议 |
4 |
5 | ### 概述
6 | TODO;
7 |
8 | ### 需求
9 | TODO;
10 |
11 | ### 方案
12 | TODO;
13 |
14 | ### 疑问
15 |
16 | 1. 为什么要引入sqlx/sqlc?
17 | TODO;
18 |
19 | ### 更新记录
20 | #### v0.0(2023-02-09) - 北野
21 | * 初始文档, 先占个位置
22 |
--------------------------------------------------------------------------------
/docs/proposal/23021310-关于paopao-ce引入bcrypt作为用户密码加密算法的设计.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23021310| 北野 | 2023-02-13 | 2023-02-13 | v0.0 | 提议 |
4 |
5 | ### 概述
6 | TODO;
7 |
8 | ### 需求
9 | TODO;
10 |
11 | ### 方案
12 | TODO;
13 |
14 | ### 疑问
15 |
16 | 1. 为什么要引入bcrypt?
17 | TODO;
18 |
19 | ### 参考文档
20 | * [bcrypt](https://github.com/golang/crypto/blob/master/bcrypt/bcrypt.go)
21 | * [bcrypt module](https://pkg.go.dev/golang.org/x/crypto@v0.6.0/bcrypt)
22 |
23 | ### 更新记录
24 | #### v0.0(2023-02-13) - 北野
25 | * 初始文档, 先占个位置
26 |
--------------------------------------------------------------------------------
/docs/proposal/23062120-关于多媒体资源URI信息存储优化.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23062120| 北野 | 2023-06-21 | 2023-06-21 | v0.1 | 提议 |
4 |
5 | ### 概述
6 | 目前图片、视频、附件资源的位置信息是使用完整url的形式存储在sql表中,也就是对外可取的url链接,这就造成这些多媒体资源与一个网站的网址进行了
7 | 绑定,如果网站变更网址后,这些媒体资源将访问不了,除非使用原先的网址。
8 |
9 | ### 需求
10 | * 变更网址不影响图片、视频、附件资源的获取;
11 | * 图片、视频、附件资源的存储位置应该对外透明,内部如何存储已经具体的存储位置信息不应该暴露到对外url链接中;
12 | * 图片、视频、附件资源的存储位置信息在sql表中应该存储为相对地址;
13 |
14 | ### 方案
15 | #### 设计要点
16 | * 图片、视频、附件资源的相对位置信息存储在sql表中;
17 | * 图片、视频、附件资源的对外url地址采用动态生成的方式,可以采用服务端生成或者客户端生成的方式或者通过api的参数来自定义;
18 |
19 | #### 设计细节
20 | TODO;
21 |
22 | ### 疑问
23 |
24 | 1. 为什么要优化?
25 | 解绑网站地址与图片、视频、附件资源的对应关系。
26 |
27 |
28 | ### 更新记录
29 | #### v0.1(2023-06-21) - 北野
30 | * 初始文档
31 |
--------------------------------------------------------------------------------
/docs/proposal/23062121-关于Migration的优化.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | 23062121| 北野 | 2023-06-21 | 2023-06-21 | v0.1 | 提议 |
4 |
5 | ### 概述
6 | 数据库表的Migration优化。
7 |
8 | ### 需求
9 | TODO;
10 |
11 | ### 方案
12 | TODO;
13 |
14 | #### 设计细节
15 | TODO;
16 |
17 | ### 疑问
18 |
19 | 1. 为什么要优化?
20 | TODO;
21 |
22 |
23 | ### 更新记录
24 | #### v0.1(2023-06-21) - 北野
25 | * 初始文档
26 |
--------------------------------------------------------------------------------
/docs/proposal/README.md:
--------------------------------------------------------------------------------
1 | ## Draft
2 | * [22110411-关于paopao-ce的设计定位](22110411-关于paopao-ce的设计定位.md "关于paopao-ce的设计定位")
3 | * [22110410-关于Friendship功能项的设计](22110410-关于Friendship功能项的设计.md "关于Friendship功能项的设计")
4 | * [22110409-关于Followship功能项的设计](22110409-关于Followship功能项的设计.md "关于Followship功能项的设计")
5 | * [22112109-引入go-mir优化后端架构设计](22112109-引入go-mir优化后端架构设计.md "引入go-mir优化后端架构设计")
6 | * [22112309-关于paopao-ce的结构设计](22112309-关于paopao-ce的结构设计.md "关于paopao-ce的结构设计")
7 |
--------------------------------------------------------------------------------
/docs/proposal/提案模板.md:
--------------------------------------------------------------------------------
1 | | 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
2 | | ----- | ----- | ----- | ----- | ----- | ----- |
3 | | <编号YYMMDDHH> | <作者> | <发表时间> | <变更时间> | <版本号v1.0> | <提议/提案/决议/冻结> |
4 |
5 | * 编号: 填写提案编号,六位数,格式为YYMMDDHH,即:年(后两位)+月+日+时。
6 | * 作者: 填写发表者。
7 | * 发表时间: 填写首次发表时间,之后保持不变。
8 | * 变更时间: 填写变更时间,首次发表时,变更时间和发表时间一样。
9 | * 版本: 当前文档版本号, 样式为v1.0/v2.0 保持两位数,微调加次版本号, 大调加大版本号。
10 | * 状态: 首次发表 状态为 **提议**; 经过讨论,多数人认同,状态为 **提案**; 审议通过,决定对方案落地,状态为 **决议**; 审议未通过,方案暂时不落地,状态变更为 **冻结**。
11 |
12 | ### <我是标题>
13 | ---- 这里写简要介绍 ----
14 |
15 | ### 场景
16 |
17 | ---- 这里描述在什么使用场景下会需要本提按 ----
18 |
19 | ### 需求
20 |
21 | ---- 这里表述具体需求,在上面描述的场景下,我们需要获得的具体状态 ----
22 |
23 | ### 方案
24 |
25 | ---- 这里提出可能的解决方案 ----
26 |
27 | ### 疑问
28 |
29 | ---- 这里描述上述内容中的疑问,比如方案落地将会遇到的坑,其他方案与本文档的比较 ----
30 |
31 | ### 更新记录
32 | #### <版本号v1.0>(<变更时间>) - <作者>
33 | * 变更描述
--------------------------------------------------------------------------------
/internal/conf/alipay.go:
--------------------------------------------------------------------------------
1 | package conf
2 |
3 | import (
4 | "sync"
5 |
6 | "github.com/sirupsen/logrus"
7 | "github.com/smartwalle/alipay/v3"
8 | )
9 |
10 | var (
11 | _alipayClient *alipay.Client
12 | _onceAlipay sync.Once
13 | )
14 |
15 | func MustAlipayClient() *alipay.Client {
16 | _onceAlipay.Do(func() {
17 | s := AlipaySetting
18 | // 将 key 的验证调整到初始化阶段
19 | client, err := alipay.New(s.AppID, s.PrivateKey, s.InProduction)
20 | if err != nil {
21 | logrus.Fatalf("alipay.New err: %s", err)
22 | }
23 | // 加载应用公钥证书
24 | if err = client.LoadAppCertPublicKeyFromFile(s.AppPublicCertFile); err != nil {
25 | logrus.Fatalf("client.LoadAppCertPublicKeyFromFile err: %s\n", err)
26 | }
27 | // 加载支付宝根证书
28 | if err = client.LoadAliPayRootCertFromFile(s.RootCertFile); err != nil {
29 | logrus.Fatalf("client.LoadAliPayRootCertFromFile err: %s\n", err)
30 | }
31 | // 加载支付宝公钥证书
32 | if err = client.LoadAlipayCertPublicKeyFromFile(s.PublicCertFile); err != nil {
33 | logrus.Fatalf("client.LoadAlipayCertPublicKeyFromFile err: %s\n", err)
34 | }
35 | _alipayClient = client
36 | })
37 | return _alipayClient
38 | }
39 |
--------------------------------------------------------------------------------
/internal/conf/cache_redis.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package conf
6 |
7 | import (
8 | "log"
9 | "sync"
10 |
11 | "github.com/redis/rueidis"
12 | )
13 |
14 | var (
15 | _redisClient rueidis.Client
16 | _onceRedis sync.Once
17 | )
18 |
19 | func MustRedisClient() rueidis.Client {
20 | _onceRedis.Do(func() {
21 | client, err := rueidis.NewClient(rueidis.ClientOption{
22 | InitAddress: redisSetting.InitAddress,
23 | Username: redisSetting.Username,
24 | Password: redisSetting.Password,
25 | SelectDB: redisSetting.SelectDB,
26 | ConnWriteTimeout: redisSetting.ConnWriteTimeout,
27 | })
28 | if err != nil {
29 | log.Fatalf("create a redis client failed: %s", err)
30 | }
31 | _redisClient = client
32 | // 顺便初始化一下CacheKeyPool
33 | initCacheKeyPool()
34 | })
35 | return _redisClient
36 | }
37 |
--------------------------------------------------------------------------------
/internal/conf/db_cgo.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build cgo
6 | // +build cgo
7 |
8 | package conf
9 |
10 | import (
11 | "database/sql"
12 |
13 | "gorm.io/driver/sqlite"
14 | "gorm.io/gorm"
15 | )
16 |
17 | const (
18 | sqlite3InCgoEnabled = true
19 | )
20 |
21 | func OpenSqlite3() (string, *sql.DB, error) {
22 | db, err := sql.Open("sqlite3", Sqlite3Setting.Dsn("sqlite3"))
23 | return "sqlite3", db, err
24 | }
25 |
26 | func gormOpenSqlite3(opts ...gorm.Option) (*gorm.DB, error) {
27 | return gorm.Open(sqlite.Open(Sqlite3Setting.Dsn("sqlite3")), opts...)
28 | }
29 |
--------------------------------------------------------------------------------
/internal/conf/db_nocgo.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !cgo
6 | // +build !cgo
7 |
8 | package conf
9 |
10 | import (
11 | "database/sql"
12 |
13 | "gorm.io/driver/sqlite"
14 | "gorm.io/gorm"
15 | _ "modernc.org/sqlite"
16 | )
17 |
18 | const (
19 | sqlite3InCgoEnabled = false
20 | )
21 |
22 | func OpenSqlite3() (string, *sql.DB, error) {
23 | db, err := sql.Open("sqlite", Sqlite3Setting.Dsn("sqlite"))
24 | return "sqlite", db, err
25 | }
26 |
27 | func gormOpenSqlite3(opts ...gorm.Option) (*gorm.DB, error) {
28 | dialector := &sqlite.Dialector{DriverName: "sqlite", DSN: Sqlite3Setting.Dsn("sqlite")}
29 | return gorm.Open(dialector, opts...)
30 | }
31 |
--------------------------------------------------------------------------------
/internal/conf/sentry.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package conf
6 |
7 | import (
8 | "time"
9 |
10 | "github.com/alimy/tryst/cfg"
11 | "github.com/getsentry/sentry-go"
12 | "github.com/rocboss/paopao-ce/pkg/version"
13 | )
14 |
15 | func initSentry() {
16 | cfg.Be("Sentry", func() {
17 | opts := sentry.ClientOptions{
18 | Dsn: sentrySetting.Dsn,
19 | Debug: sentrySetting.Debug,
20 | AttachStacktrace: sentrySetting.AttachStacktrace,
21 | TracesSampleRate: sentrySetting.TracesSampleRate,
22 | }
23 | _ = sentry.Init(opts)
24 | if sentrySetting.AttachLogrus {
25 | setupSentryLogrus(opts)
26 | }
27 | sentry.WithScope(func(scope *sentry.Scope) {
28 | scope.SetExtras(map[string]any{
29 | "version": version.VersionInfo(),
30 | "time": time.Now().Local(),
31 | })
32 | sentry.CaptureMessage("paopao-ce sentry works!")
33 | })
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/internal/core/authority.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/ms"
9 | )
10 |
11 | // AuthorizationManageService 授权管理服务
12 | type AuthorizationManageService interface {
13 | IsAllow(user *ms.User, action *ms.Action) bool
14 | BeFriendFilter(userId int64) ms.FriendFilter
15 | BeFriendIds(userId int64) ([]int64, error)
16 | MyFriendSet(userId int64) ms.FriendSet
17 | }
18 |
--------------------------------------------------------------------------------
/internal/core/core.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | // DataService 数据服务集成
8 | type DataService interface {
9 | // 钱包服务
10 | WalletService
11 |
12 | // 消息服务
13 | MessageService
14 |
15 | // 话题服务
16 | TopicService
17 |
18 | // 推文服务
19 | TweetService
20 | TweetManageService
21 | TweetHelpService
22 |
23 | // 推文指标服务
24 | UserMetricServantA
25 | TweetMetricServantA
26 | CommentMetricServantA
27 |
28 | // 动态信息相关服务
29 | TrendsManageServantA
30 |
31 | // 评论服务
32 | CommentService
33 | CommentManageService
34 |
35 | // 用户服务
36 | UserManageService
37 | ContactManageService
38 | FollowingManageService
39 | UserRelationService
40 |
41 | // 安全服务
42 | SecurityService
43 | AttachmentCheckService
44 | }
45 |
46 | // WebDataServantA Web数据服务集成(版本A)
47 | type WebDataServantA interface {
48 | // 话题服务
49 | TopicServantA
50 |
51 | // 推文服务
52 | TweetServantA
53 | TweetManageServantA
54 | TweetHelpServantA
55 | }
56 |
--------------------------------------------------------------------------------
/internal/core/cs/comments.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | const (
8 | StyleCommentDefault StyleCommentType = iota
9 | StyleCommentHots
10 | StyleCommentNewest
11 | )
12 |
13 | type StyleCommentType uint8
14 |
15 | type CommentThumbs struct {
16 | UserID int64 `json:"user_id"`
17 | TweetID int64 `json:"tweet_id"`
18 | CommentID int64 `json:"comment_id"`
19 | ReplyID int64 `json:"reply_id"`
20 | CommentType int8 `json:"comment_type"`
21 | IsThumbsUp int8 `json:"is_thumbs_up"`
22 | IsThumbsDown int8 `json:"is_thumbs_down"`
23 | }
24 |
25 | type CommentThumbsList []*CommentThumbs
26 |
27 | type CommentThumbsMap map[int64]*CommentThumbs
28 |
--------------------------------------------------------------------------------
/internal/core/cs/contact.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | const (
8 | ContactStatusRequesting int8 = iota + 1
9 | ContactStatusAgree
10 | ContactStatusReject
11 | ContactStatusDeleted
12 | )
13 |
14 | type Contact struct {
15 | ID int64 `db:"id" json:"id"`
16 | UserId int64 `db:"user_id" json:"user_id"`
17 | FriendId int64 `db:"friend_id" json:"friend_id"`
18 | GroupId int64 `json:"group_id"`
19 | Remark string `json:"remark"`
20 | Status int8 `json:"status"` // 1请求好友, 2已同意好友, 3已拒绝好友, 4已删除好友
21 | IsTop int8 `json:"is_top"`
22 | IsBlack int8 `json:"is_black"`
23 | NoticeEnable int8 `json:"notice_enable"`
24 | IsDel int8 `json:"-"`
25 | DeletedOn int64 `db:"-" json:"-"`
26 | }
27 |
--------------------------------------------------------------------------------
/internal/core/cs/cs.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package cs contain core data service interface type
6 | // model define
7 |
8 | package cs
9 |
--------------------------------------------------------------------------------
/internal/core/cs/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | import "errors"
8 |
9 | // internal core error variable for data logic implement.
10 | var (
11 | ErrNotImplemented = errors.New("not implemented")
12 | ErrNoPermission = errors.New("no permission")
13 | )
14 |
--------------------------------------------------------------------------------
/internal/core/cs/messages.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | const (
8 | // 消息列表样式
9 | StyleMsgAll MessageStyle = "all"
10 | StyleMsgSystem MessageStyle = "system"
11 | StyleMsgWhisper MessageStyle = "whisper"
12 | StyleMsgRequesting MessageStyle = "requesting"
13 | StyleMsgUnread MessageStyle = "unread"
14 | )
15 |
16 | type MessageStyle string
17 |
--------------------------------------------------------------------------------
/internal/core/cs/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | const (
8 | MetricActionCreateTweet uint8 = iota
9 | MetricActionDeleteTweet
10 | )
11 |
12 | type TweetMetric struct {
13 | PostId int64
14 | CommentCount int64
15 | UpvoteCount int64
16 | CollectionCount int64
17 | ShareCount int64
18 | ThumbsUpCount int64
19 | ThumbsDownCount int64
20 | }
21 |
22 | type CommentMetric struct {
23 | CommentId int64
24 | ReplyCount int32
25 | ThumbsUpCount int32
26 | ThumbsDownCount int32
27 | }
28 |
29 | func (m *TweetMetric) RankScore(motivationFactor int) int64 {
30 | if motivationFactor == 0 {
31 | motivationFactor = 1
32 | }
33 | return (m.CommentCount + m.UpvoteCount*2 + m.CollectionCount*4 + m.ShareCount*8) * int64(motivationFactor)
34 | }
35 |
36 | func (m *CommentMetric) RankScore(motivationFactor int) int64 {
37 | if motivationFactor == 0 {
38 | motivationFactor = 1
39 | }
40 | return int64(m.ReplyCount*2+m.ThumbsUpCount*4-m.ThumbsDownCount) * int64(motivationFactor)
41 | }
42 |
--------------------------------------------------------------------------------
/internal/core/cs/search.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | const (
8 | // 搜索查询类型
9 | TsQueryTypeDefault TsQueryType = "search"
10 | TsQueryTypeTag TsQueryType = "tag"
11 | )
12 |
13 | type (
14 | // TsQueryType 搜索查询类型
15 | TsQueryType string
16 |
17 | // TsDocList 索引条陈列表
18 | TsDocList []TsDocItem
19 | )
20 |
21 | // TsQueryReq 搜索查询请求
22 | type TsQueryReq struct {
23 | Query string
24 | Visibility []TweetVisibleType
25 | Type TsQueryType
26 | }
27 |
28 | // TsQueryResp 搜索查询响应
29 | type TsQueryResp struct {
30 | Items TweetList
31 | Total int64
32 | }
33 |
34 | // TsDocItem 索引条陈
35 | type TsDocItem struct {
36 | Post *TweetInfo
37 | Content string
38 | }
39 |
--------------------------------------------------------------------------------
/internal/core/cs/timeline.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | // TweetBox 推文列表盒子,包含其他一些关于推文列表的信息
8 | type TweetBox struct {
9 | Tweets TweetList
10 | Total int64
11 | }
12 |
--------------------------------------------------------------------------------
/internal/core/cs/trading.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | type AttachmentBill struct {
8 | ID int64 `json:"id"`
9 | PostID int64 `json:"post_id"`
10 | UserID int64 `json:"user_id"`
11 | PaidAmount int64 `json:"paid_amount"`
12 | }
13 |
--------------------------------------------------------------------------------
/internal/core/cs/trends.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package cs
6 |
7 | import "github.com/rocboss/paopao-ce/pkg/types"
8 |
9 | type TrendsItem struct {
10 | Username string `json:"username"`
11 | Nickname string `json:"nickname"`
12 | Avatar string `json:"avatar"`
13 | IsFresh bool `json:"is_fresh" gorm:"-"`
14 | }
15 |
16 | func DistinctTrends(items []*TrendsItem) []*TrendsItem {
17 | if len(items) == 0 {
18 | return items
19 | }
20 | res := make([]*TrendsItem, 0, len(items))
21 | set := make(map[string]types.Empty, len(items))
22 | for _, item := range items {
23 | if _, exist := set[item.Username]; exist {
24 | continue
25 | }
26 | res = append(res, item)
27 | set[item.Username] = types.Empty{}
28 | }
29 | return res
30 | }
31 |
--------------------------------------------------------------------------------
/internal/core/messages.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/cs"
9 | "github.com/rocboss/paopao-ce/internal/core/ms"
10 | )
11 |
12 | // MessageService 消息服务
13 | type MessageService interface {
14 | CreateMessage(msg *ms.Message) (*ms.Message, error)
15 | GetUnreadCount(userID int64) (int64, error)
16 | GetMessageByID(id int64) (*ms.Message, error)
17 | ReadMessage(message *ms.Message) error
18 | ReadAllMessage(userId int64) error
19 | GetMessages(userId int64, style cs.MessageStyle, limit, offset int) ([]*ms.MessageFormated, int64, error)
20 | }
21 |
--------------------------------------------------------------------------------
/internal/core/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/cs"
9 | )
10 |
11 | type TweetMetricServantA interface {
12 | UpdateTweetMetric(metric *cs.TweetMetric) error
13 | AddTweetMetric(postId int64) error
14 | DeleteTweetMetric(postId int64) error
15 | }
16 |
17 | type CommentMetricServantA interface {
18 | UpdateCommentMetric(metric *cs.CommentMetric) error
19 | AddCommentMetric(commentId int64) error
20 | DeleteCommentMetric(commentId int64) error
21 | }
22 |
23 | type UserMetricServantA interface {
24 | UpdateUserMetric(userId int64, action uint8) error
25 | AddUserMetric(userId int64) error
26 | DeleteUserMetric(userId int64) error
27 | }
28 |
--------------------------------------------------------------------------------
/internal/core/ms/comments.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
9 | )
10 |
11 | type (
12 | Comment = dbr.Comment
13 | CommentFormated = dbr.CommentFormated
14 | CommentReply = dbr.CommentReply
15 | CommentContent = dbr.CommentContent
16 | CommentReplyFormated = dbr.CommentReplyFormated
17 | )
18 |
--------------------------------------------------------------------------------
/internal/core/ms/messages.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
9 | )
10 |
11 | const (
12 | MsgTypePost = dbr.MsgTypePost
13 | MsgtypeComment = dbr.MsgtypeComment
14 | MsgTypeReply = dbr.MsgTypeReply
15 | MsgTypeWhisper = dbr.MsgTypeWhisper
16 | MsgTypeRequestingFriend = dbr.MsgTypeRequestingFriend
17 | MsgTypeSystem = dbr.MsgTypeSystem
18 |
19 | MsgStatusUnread = dbr.MsgStatusUnread
20 | MsgStatusReaded = dbr.MsgStatusReaded
21 | )
22 |
23 | type (
24 | Message = dbr.Message
25 | MessageFormated = dbr.MessageFormated
26 | )
27 |
--------------------------------------------------------------------------------
/internal/core/ms/ms.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package ms contain core data service interface type
6 | // model define for gorm adapter
7 | package ms
8 |
9 | import (
10 | "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
11 | )
12 |
13 | const (
14 | UserStatusNormal = dbr.UserStatusNormal
15 | UserStatusClosed = dbr.UserStatusClosed
16 | )
17 |
18 | type (
19 | User = dbr.User
20 | Post = dbr.Post
21 | ConditionsT = dbr.ConditionsT
22 | PostFormated = dbr.PostFormated
23 | UserFormated = dbr.UserFormated
24 | PostContentFormated = dbr.PostContentFormated
25 | Model = dbr.Model
26 | )
27 |
--------------------------------------------------------------------------------
/internal/core/ms/security.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
9 | )
10 |
11 | type (
12 | Captcha = dbr.Captcha
13 | )
14 |
--------------------------------------------------------------------------------
/internal/core/ms/timeline.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | type IndexTweetList struct {
8 | Tweets []*PostFormated
9 | Total int64
10 | }
11 |
--------------------------------------------------------------------------------
/internal/core/ms/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | type (
8 | ContactItem struct {
9 | UserId int64 `json:"user_id"`
10 | Username string `db:"username" json:"username"`
11 | Nickname string `json:"nickname"`
12 | Avatar string `json:"avatar"`
13 | Phone string `json:"phone,omitempty"`
14 | IsFollowing bool `json:"is_following"`
15 | CreatedOn int64 `json:"created_on"`
16 | }
17 |
18 | ContactList struct {
19 | Contacts []ContactItem `json:"contacts"`
20 | Total int64 `json:"total"`
21 | }
22 | )
23 |
--------------------------------------------------------------------------------
/internal/core/ms/wallet.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package ms
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
9 | )
10 |
11 | type (
12 | WalletStatement = dbr.WalletStatement
13 | WalletRecharge = dbr.WalletRecharge
14 | )
15 |
--------------------------------------------------------------------------------
/internal/core/security.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "time"
9 |
10 | "github.com/rocboss/paopao-ce/internal/core/ms"
11 | )
12 |
13 | // SecurityService 安全相关服务
14 | type SecurityService interface {
15 | GetLatestPhoneCaptcha(phone string) (*ms.Captcha, error)
16 | UsePhoneCaptcha(captcha *ms.Captcha) error
17 | SendPhoneCaptcha(phone string) error
18 | }
19 |
20 | // AttachmentCheckService 附件检测服务
21 | type AttachmentCheckService interface {
22 | CheckAttachment(uri string) error
23 | }
24 |
25 | // PhoneVerifyService 手机验证服务
26 | type PhoneVerifyService interface {
27 | SendPhoneCaptcha(phone string, captcha string, expire time.Duration) error
28 | }
29 |
--------------------------------------------------------------------------------
/internal/core/storage.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "io"
9 | )
10 |
11 | // ObjectStorageService storage service interface that implement base AliOSS、MINIO or other
12 | type ObjectStorageService interface {
13 | OssCreateService
14 | OssDeleteService
15 |
16 | SignURL(objectKey string, expiredInSec int64) (string, error)
17 | ObjectURL(objetKey string) string
18 | ObjectKey(cUrl string) string
19 | }
20 |
21 | // OssCreateService Object Storage System Object Create service
22 | type OssCreateService interface {
23 | PutObject(objectKey string, reader io.Reader, objectSize int64, contentType string, persistance bool) (string, error)
24 | PersistObject(objectKey string) error
25 | }
26 |
27 | // OssCreateService Object Storage System Object Delete service
28 | type OssDeleteService interface {
29 | DeleteObject(objectKey string) error
30 | DeleteObjects(objectKeys []string) error
31 | IsObjectExist(objectKey string) (bool, error)
32 | }
33 |
--------------------------------------------------------------------------------
/internal/core/timeline.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/cs"
9 | "github.com/rocboss/paopao-ce/internal/core/ms"
10 | )
11 |
12 | // IndexPostsService 广场首页推文列表服务
13 | type IndexPostsService interface {
14 | IndexPosts(user *ms.User, offset int, limit int) (*ms.IndexTweetList, error)
15 | }
16 |
17 | // IndexPostsServantA 广场首页推文列表服务(版本A)
18 | type IndexPostsServantA interface {
19 | IndexPosts(user *ms.User, limit int, offset int) (*cs.TweetBox, error)
20 | }
21 |
--------------------------------------------------------------------------------
/internal/core/trends.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/cs"
9 | )
10 |
11 | // TrendsManageServantA 动态信息管理服务
12 | type TrendsManageServantA interface {
13 | GetIndexTrends(userId int64, limit int, offset int) ([]*cs.TrendsItem, int64, error)
14 | }
15 |
--------------------------------------------------------------------------------
/internal/core/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/Masterminds/semver/v3"
9 | )
10 |
11 | // VersionInfo 版本信息
12 | type VersionInfo interface {
13 | Name() string
14 | Version() *semver.Version
15 | }
16 |
--------------------------------------------------------------------------------
/internal/core/wallet.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package core
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/core/ms"
9 | )
10 |
11 | // WalletService wallet service interface
12 | type WalletService interface {
13 | GetUserWalletBills(userID int64, offset, limit int) ([]*ms.WalletStatement, error)
14 | GetUserWalletBillCount(userID int64) (int64, error)
15 | GetRechargeByID(id int64) (*ms.WalletRecharge, error)
16 | CreateRecharge(userId, amount int64) (*ms.WalletRecharge, error)
17 | HandleRechargeSuccess(recharge *ms.WalletRecharge, tradeNo string) error
18 | HandlePostAttachmentBought(post *ms.Post, user *ms.User) error
19 | }
20 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/attachment.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | import "gorm.io/gorm"
8 |
9 | type AttachmentType int
10 |
11 | const (
12 | AttachmentTypeImage AttachmentType = iota + 1
13 | AttachmentTypeVideo
14 | AttachmentTypeOther
15 | )
16 |
17 | type Attachment struct {
18 | *Model
19 | UserID int64 `json:"user_id"`
20 | FileSize int64 `json:"file_size"`
21 | ImgWidth int `json:"img_width"`
22 | ImgHeight int `json:"img_height"`
23 | Type AttachmentType `json:"type"`
24 | Content string `json:"content"`
25 | }
26 |
27 | func (a *Attachment) Create(db *gorm.DB) (*Attachment, error) {
28 | err := db.Create(&a).Error
29 |
30 | return a, err
31 | }
32 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/captcha.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | import "gorm.io/gorm"
8 |
9 | type Captcha struct {
10 | *Model
11 | Phone string `json:"phone"`
12 | Captcha string `json:"captcha"`
13 | UseTimes int `json:"use_times"`
14 | ExpiredOn int64 `json:"expired_on"`
15 | }
16 |
17 | func (c *Captcha) Create(db *gorm.DB) (*Captcha, error) {
18 | err := db.Create(&c).Error
19 |
20 | return c, err
21 | }
22 |
23 | func (c *Captcha) Update(db *gorm.DB) error {
24 | return db.Model(&Captcha{}).Where("id = ? AND is_del = ?", c.Model.ID, 0).Save(c).Error
25 | }
26 |
27 | func (c *Captcha) Get(db *gorm.DB) (*Captcha, error) {
28 | var captcha Captcha
29 | if c.Model != nil && c.ID > 0 {
30 | db = db.Where("id = ? AND is_del = ?", c.ID, 0)
31 | }
32 | if c.Phone != "" {
33 | db = db.Where("phone = ?", c.Phone)
34 | }
35 |
36 | err := db.Last(&captcha).Error
37 | if err != nil {
38 | return &captcha, err
39 | }
40 |
41 | return &captcha, nil
42 | }
43 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/comment_thumbs.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | type TweetCommentThumbs struct {
8 | *Model
9 | UserID int64 `json:"user_id"`
10 | TweetID int64 `json:"tweet_id"`
11 | CommentID int64 `json:"comment_id"`
12 | ReplyID int64 `json:"reply_id"`
13 | CommentType int8 `json:"comment_type"`
14 | IsThumbsUp int8 `json:"is_thumbs_up"`
15 | IsThumbsDown int8 `json:"is_thumbs_down"`
16 | }
17 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/model.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | import (
8 | "time"
9 |
10 | "gorm.io/gorm"
11 | "gorm.io/plugin/soft_delete"
12 | )
13 |
14 | // Model 公共Model
15 | type Model struct {
16 | ID int64 `gorm:"primary_key" json:"id"`
17 | CreatedOn int64 `json:"created_on"`
18 | ModifiedOn int64 `json:"modified_on"`
19 | DeletedOn int64 `json:"deleted_on"`
20 | IsDel soft_delete.DeletedAt `gorm:"softDelete:flag" json:"is_del"`
21 | }
22 |
23 | type ConditionsT map[string]any
24 | type Predicates map[string][]any
25 |
26 | func (m *Model) BeforeCreate(tx *gorm.DB) (err error) {
27 | nowTime := time.Now().Unix()
28 |
29 | tx.Statement.SetColumn("created_on", nowTime)
30 | tx.Statement.SetColumn("modified_on", nowTime)
31 | return
32 | }
33 |
34 | func (m *Model) BeforeUpdate(tx *gorm.DB) (err error) {
35 | if !tx.Statement.Changed("modified_on") {
36 | tx.Statement.SetColumn("modified_on", time.Now().Unix())
37 | }
38 |
39 | return
40 | }
41 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/post_attachment_bill.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | import "gorm.io/gorm"
8 |
9 | type PostAttachmentBill struct {
10 | *Model
11 | PostID int64 `json:"post_id"`
12 | UserID int64 `json:"user_id"`
13 | PaidAmount int64 `json:"paid_amount"`
14 | }
15 |
16 | func (p *PostAttachmentBill) Get(db *gorm.DB) (*PostAttachmentBill, error) {
17 | var pas PostAttachmentBill
18 | if p.Model != nil && p.ID > 0 {
19 | db = db.Where("id = ? AND is_del = ?", p.ID, 0)
20 | }
21 | if p.PostID > 0 {
22 | db = db.Where("post_id = ?", p.PostID)
23 | }
24 | if p.UserID > 0 {
25 | db = db.Where("user_id = ?", p.UserID)
26 | }
27 |
28 | err := db.First(&pas).Error
29 | if err != nil {
30 | return &pas, err
31 | }
32 |
33 | return &pas, nil
34 | }
35 |
36 | func (p *PostAttachmentBill) Create(db *gorm.DB) (*PostAttachmentBill, error) {
37 | err := db.Create(&p).Error
38 |
39 | return p, err
40 | }
41 |
--------------------------------------------------------------------------------
/internal/dao/jinzhu/dbr/wallet_recharge.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package dbr
6 |
7 | import "gorm.io/gorm"
8 |
9 | type WalletRecharge struct {
10 | *Model
11 | UserID int64 `json:"user_id"`
12 | Amount int64 `json:"amount"`
13 | TradeNo string `json:"trade_no"`
14 | TradeStatus string `json:"trade_status"`
15 | }
16 |
17 | func (p *WalletRecharge) Get(db *gorm.DB) (*WalletRecharge, error) {
18 | var pas WalletRecharge
19 | if p.Model != nil && p.ID > 0 {
20 | db = db.Where("id = ? AND is_del = ?", p.ID, 0)
21 | }
22 | if p.UserID > 0 {
23 | db = db.Where("user_id = ?", p.UserID)
24 | }
25 |
26 | err := db.First(&pas).Error
27 | if err != nil {
28 | return &pas, err
29 | }
30 |
31 | return &pas, nil
32 | }
33 |
34 | func (p *WalletRecharge) Create(db *gorm.DB) (*WalletRecharge, error) {
35 | err := db.Create(&p).Error
36 |
37 | return p, err
38 | }
39 |
--------------------------------------------------------------------------------
/internal/dao/sakila/sakila.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Core service implement base sqlx+mysql. All sub-service
6 | // will declare here and provide initial function.
7 |
8 | package sakila
9 |
10 | import (
11 | "github.com/rocboss/paopao-ce/internal/core"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func NewDataService() (core.DataService, core.VersionInfo) {
16 | logrus.Fatal("not support now")
17 | return nil, nil
18 | }
19 |
20 | func NewWebDataServantA() (core.WebDataServantA, core.VersionInfo) {
21 | logrus.Fatal("not support now")
22 | return nil, nil
23 | }
24 |
25 | func NewAuthorizationManageService() core.AuthorizationManageService {
26 | logrus.Fatal("not support now")
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/internal/dao/security/attachment_check.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package security
6 |
7 | import (
8 | "fmt"
9 | "strings"
10 |
11 | "github.com/rocboss/paopao-ce/internal/conf"
12 | "github.com/rocboss/paopao-ce/internal/core"
13 | )
14 |
15 | var (
16 | _ core.AttachmentCheckService = (*attachmentCheckServant)(nil)
17 | )
18 |
19 | type attachmentCheckServant struct {
20 | domain string
21 | }
22 |
23 | func (s *attachmentCheckServant) CheckAttachment(uri string) error {
24 | if strings.Index(uri, s.domain) != 0 {
25 | return fmt.Errorf("附件非本站资源")
26 | }
27 | return nil
28 | }
29 |
30 | func NewAttachmentCheckService() core.AttachmentCheckService {
31 | return &attachmentCheckServant{
32 | domain: conf.GetOssDomain(),
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/internal/dao/security/security.go:
--------------------------------------------------------------------------------
1 | package security
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/alimy/tryst/cfg"
7 | "github.com/rocboss/paopao-ce/internal/core"
8 | )
9 |
10 | func NewPhoneVerifyService() core.PhoneVerifyService {
11 | smsVendor, _ := cfg.Val("sms")
12 | switch strings.ToLower(smsVendor) {
13 | case "smsjuhe":
14 | return newJuheSmsServant()
15 | default:
16 | return newJuheSmsServant()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/internal/dao/slonik/slonik.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Core service implement base sqlx+postgresql. All sub-service
6 | // will declare here and provide initial function.
7 |
8 | package slonik
9 |
10 | import (
11 | "github.com/rocboss/paopao-ce/internal/core"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func NewDataService() (core.DataService, core.VersionInfo) {
16 | logrus.Fatal("not support now")
17 | return nil, nil
18 | }
19 |
20 | func NewWebDataServantA() (core.WebDataServantA, core.VersionInfo) {
21 | logrus.Fatal("not support now")
22 | return nil, nil
23 | }
24 |
25 | func NewAuthorizationManageService() core.AuthorizationManageService {
26 | logrus.Fatal("not support now")
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/internal/infra/infra.go:
--------------------------------------------------------------------------------
1 | // Copyright 2025 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package infra infrastructure components.
6 |
7 | package infra
8 |
--------------------------------------------------------------------------------
/internal/infra/metrics/metrics_tryst.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package metrics
6 |
7 | import (
8 | "github.com/alimy/tryst/event"
9 | "github.com/alimy/tryst/pool"
10 | )
11 |
12 | type simpleMetricManager struct {
13 | mm event.EventManager
14 | }
15 |
16 | func (s *simpleMetricManager) Start() {
17 | s.mm.Start()
18 | }
19 |
20 | func (s *simpleMetricManager) Stop() {
21 | s.mm.Stop()
22 | }
23 |
24 | func (s *simpleMetricManager) OnMeasure(metric Metric) {
25 | s.mm.OnEvent(metric)
26 | }
27 |
28 | func NewMetricManager(fn pool.RespFn[Metric], opts ...pool.Option) MetricManager {
29 | return &simpleMetricManager{
30 | mm: event.NewEventManager(fn, opts...),
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/internal/infra/metrics/statistics/cache.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package statistics
6 |
7 | import (
8 | "sync"
9 |
10 | "github.com/rocboss/paopao-ce/internal/core"
11 | )
12 |
13 | var (
14 | _metricCache core.MetricCache
15 | _onceMetricCache sync.Once
16 | )
17 |
18 | type metricCache struct {
19 | eventTempWorkerCount map[string]int32
20 | }
21 |
22 | func (m *metricCache) SetEventTempWorkerCount(name string, count int32) {
23 | // 直接赋值,不需要加锁,因为这仅仅是一个统计信息
24 | m.eventTempWorkerCount[name] = count
25 | }
26 |
27 | func (m *metricCache) GetEventTempWorkerCount(name string) int32 {
28 | return m.eventTempWorkerCount[name]
29 | }
30 |
31 | func NewMetricCache() core.MetricCache {
32 | _onceMetricCache.Do(func() {
33 | _metricCache = &metricCache{
34 | eventTempWorkerCount: make(map[string]int32),
35 | }
36 | })
37 | return _metricCache
38 | }
39 |
--------------------------------------------------------------------------------
/internal/infra/migration/migration.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !migration
6 | // +build !migration
7 |
8 | package migration
9 |
10 | import (
11 | "github.com/alimy/tryst/cfg"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func Run() {
16 | if cfg.If("Migration") {
17 | logrus.Infoln("want migrate feature but not support in this compile version")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/internal/internal.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package internal
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/infra/events"
9 | "github.com/rocboss/paopao-ce/internal/infra/metrics"
10 | "github.com/rocboss/paopao-ce/internal/infra/migration"
11 | )
12 |
13 | func Initial() {
14 | // migrate database if needed
15 | migration.Run()
16 | // event manager system initialize
17 | events.Initial()
18 | // metric manager system initialize
19 | metrics.Initial()
20 | }
21 |
--------------------------------------------------------------------------------
/internal/model/enum/builtin.go:
--------------------------------------------------------------------------------
1 | package enum
2 |
3 | //go:generate go-enum --names --values --nocase --marshal --mustparse --sqlnullint
4 |
5 | /*
6 | Boolean
7 | ENUM(
8 |
9 | False, // false
10 | True, // true
11 |
12 | )
13 | */
14 | type Boolean int
15 |
--------------------------------------------------------------------------------
/internal/model/enum/enum.go:
--------------------------------------------------------------------------------
1 | // Copyright 2025 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package enum all const enum variables define.
6 |
7 | package enum
8 |
--------------------------------------------------------------------------------
/internal/model/joint/joint.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // Package joint provider some common base type or logic for model define.
6 |
7 | package joint
8 |
9 | type BasePageInfo struct {
10 | Page int `form:"-" binding:"-"`
11 | PageSize int `form:"-" binding:"-"`
12 | }
13 |
14 | func (r *BasePageInfo) SetPageInfo(page int, pageSize int) {
15 | r.Page, r.PageSize = page, pageSize
16 | }
17 |
18 | type JsonResp struct {
19 | Code int `json:"code"`
20 | Msg string `json:"msg,omitempty"`
21 | Data any `json:"data,omitempty"`
22 | }
23 |
--------------------------------------------------------------------------------
/internal/model/joint/json.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package joint
6 |
7 | import (
8 | stdJson "encoding/json"
9 | "net/http"
10 |
11 | "github.com/gin-gonic/gin"
12 | "github.com/rocboss/paopao-ce/pkg/json"
13 | )
14 |
15 | type CachePageResp struct {
16 | Data *PageResp
17 | JsonResp stdJson.RawMessage
18 | }
19 |
20 | func (r *CachePageResp) Render(c *gin.Context) {
21 | if len(r.JsonResp) != 0 {
22 | c.JSON(http.StatusOK, r.JsonResp)
23 | } else {
24 | c.JSON(http.StatusOK, &JsonResp{
25 | Code: 0,
26 | Msg: "success",
27 | Data: r.Data,
28 | })
29 | }
30 | }
31 |
32 | func RespMarshal(data any) (stdJson.RawMessage, error) {
33 | return json.Marshal(data)
34 | }
35 |
--------------------------------------------------------------------------------
/internal/model/joint/page.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package joint
6 |
7 | type Pager struct {
8 | Page int `json:"page"`
9 | PageSize int `json:"page_size"`
10 | TotalRows int64 `json:"total_rows"`
11 | }
12 |
13 | type PageResp struct {
14 | List any `json:"list"`
15 | Pager Pager `json:"pager"`
16 | }
17 |
18 | func PageRespFrom(list any, page int, pageSize int, totalRows int64) *PageResp {
19 | return &PageResp{
20 | List: list,
21 | Pager: Pager{
22 | Page: page,
23 | PageSize: pageSize,
24 | TotalRows: totalRows,
25 | },
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/model/model.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package model
6 |
--------------------------------------------------------------------------------
/internal/model/web/admin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | type ChangeUserStatusReq struct {
8 | BaseInfo `json:"-" binding:"-"`
9 | ID int64 `json:"id" form:"id" binding:"required"`
10 | Status int `json:"status" form:"status" binding:"required,oneof=1 2"`
11 | }
12 |
13 | type SiteInfoReq struct {
14 | SimpleInfo `json:"-" binding:"-"`
15 | }
16 |
17 | type SiteInfoResp struct {
18 | RegisterUserCount int64 `json:"register_user_count"`
19 | OnlineUserCount int `json:"online_user_count"`
20 | HistoryMaxOnline int `json:"history_max_online"`
21 | ServerUpTime int64 `json:"server_up_time"`
22 | }
23 |
--------------------------------------------------------------------------------
/internal/model/web/audit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | const (
8 | AuditStyleUnknown AuditStyle = iota
9 | AuditStyleUserTweet
10 | AuditStyleUserTweetComment
11 | AuditStyleUserTweetReply
12 | )
13 |
14 | const (
15 | AuditHookCtxKey = "audit_ctx_key"
16 | OnlineUserCtxKey = "online_user_ctx_key"
17 | )
18 |
19 | type AuditStyle uint8
20 |
21 | type AuditMetaInfo struct {
22 | Style AuditStyle
23 | Id int64
24 | }
25 |
26 | func (s AuditStyle) String() (res string) {
27 | switch s {
28 | case AuditStyleUserTweet:
29 | res = "UserTweet"
30 | case AuditStyleUserTweetComment:
31 | res = "UserTweetComment"
32 | case AuditStyleUserTweetReply:
33 | res = "UserTweetReply"
34 | case AuditStyleUnknown:
35 | fallthrough
36 | default:
37 | res = "Unknown"
38 | }
39 | return
40 | }
41 |
--------------------------------------------------------------------------------
/internal/model/web/followship.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/model/joint"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | type FollowUserReq struct {
13 | BaseInfo `json:"-" binding:"-"`
14 | UserId int64 `json:"user_id" binding:"required"`
15 | }
16 |
17 | type UnfollowUserReq struct {
18 | BaseInfo `json:"-" binding:"-"`
19 | UserId int64 `json:"user_id" binding:"required"`
20 | }
21 |
22 | type ListFollowsReq struct {
23 | BaseInfo `json:"-" binding:"-"`
24 | joint.BasePageInfo
25 | Username string `form:"username" binding:"required"`
26 | }
27 |
28 | type ListFollowsResp base.PageResp
29 |
30 | type ListFollowingsReq struct {
31 | BaseInfo `form:"-" binding:"-"`
32 | joint.BasePageInfo
33 | Username string `form:"username" binding:"required"`
34 | }
35 |
36 | type ListFollowingsResp base.PageResp
37 |
--------------------------------------------------------------------------------
/internal/model/web/friendship.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/servants/base"
9 | )
10 |
11 | type RequestingFriendReq struct {
12 | BaseInfo `json:"-" binding:"-"`
13 | UserId int64 `json:"user_id" binding:"required"`
14 | Greetings string `json:"greetings" binding:"required"`
15 | }
16 |
17 | type AddFriendReq struct {
18 | BaseInfo `json:"-" binding:"-"`
19 | UserId int64 `json:"user_id" binding:"required"`
20 | }
21 |
22 | type RejectFriendReq struct {
23 | BaseInfo `json:"-" binding:"-"`
24 | UserId int64 `json:"user_id" binding:"required"`
25 | }
26 |
27 | type DeleteFriendReq struct {
28 | BaseInfo `json:"-" binding:"-"`
29 | UserId int64 `json:"user_id"`
30 | }
31 |
32 | type GetContactsReq struct {
33 | BaseInfo `form:"-" binding:"-"`
34 | Page int `form:"-" binding:"-"`
35 | PageSize int `form:"-" binding:"-"`
36 | }
37 |
38 | type GetContactsResp base.PageResp
39 |
40 | func (r *GetContactsReq) SetPageInfo(page int, pageSize int) {
41 | r.Page, r.PageSize = page, pageSize
42 | }
43 |
--------------------------------------------------------------------------------
/internal/model/web/pub.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | type GetCaptchaResp struct {
8 | Id string `json:"id"`
9 | Content string `json:"b64s"`
10 | }
11 |
12 | type SendCaptchaReq struct {
13 | Phone string `json:"phone" form:"phone" binding:"required"`
14 | ImgCaptcha string `json:"img_captcha" form:"img_captcha" binding:"required"`
15 | ImgCaptchaID string `json:"img_captcha_id" form:"img_captcha_id" binding:"required"`
16 | }
17 |
18 | type LoginReq struct {
19 | Username string `json:"username" form:"username" binding:"required"`
20 | Password string `json:"password" form:"password" binding:"required"`
21 | }
22 |
23 | type LoginResp struct {
24 | Token string `json:"token"`
25 | }
26 |
27 | type RegisterReq struct {
28 | Username string `json:"username" form:"username" binding:"required"`
29 | Password string `json:"password" form:"password" binding:"required"`
30 | }
31 |
32 | type RegisterResp struct {
33 | UserId int64 `json:"id"`
34 | Username string `json:"username"`
35 | }
36 |
--------------------------------------------------------------------------------
/internal/model/web/relax.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | "encoding/json"
9 | "net/http"
10 |
11 | "github.com/gin-gonic/gin"
12 | "github.com/rocboss/paopao-ce/internal/model/joint"
13 | )
14 |
15 | type GetUnreadMsgCountReq struct {
16 | SimpleInfo `json:"-" binding:"-"`
17 | }
18 |
19 | type GetUnreadMsgCountResp struct {
20 | Count int64 `json:"count"`
21 | JsonResp json.RawMessage `json:"-"`
22 | }
23 |
24 | func (r *GetUnreadMsgCountResp) Render(c *gin.Context) {
25 | if len(r.JsonResp) != 0 {
26 | c.JSON(http.StatusOK, r.JsonResp)
27 | } else {
28 | c.JSON(http.StatusOK, &joint.JsonResp{
29 | Code: 0,
30 | Msg: "success",
31 | Data: r,
32 | })
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/internal/model/web/site.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/conf"
9 | "github.com/rocboss/paopao-ce/pkg/version"
10 | )
11 |
12 | type VersionResp struct {
13 | BuildInfo *version.BuildInfo `json:"build_info"`
14 | }
15 |
16 | type SiteProfileResp = conf.WebProfileConf
17 |
--------------------------------------------------------------------------------
/internal/model/web/trends.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/model/joint"
9 | )
10 |
11 | type GetIndexTrendsReq struct {
12 | SimpleInfo `json:"-" binding:"-"`
13 | joint.BasePageInfo
14 | }
15 |
16 | type GetIndexTrendsResp struct {
17 | joint.CachePageResp
18 | }
19 |
--------------------------------------------------------------------------------
/internal/servants/admin/admin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package admin
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | api "github.com/rocboss/paopao-ce/auto/api/m/v1"
10 | )
11 |
12 | // RouteWeb register Manager route
13 | func RouteManager(e *gin.Engine) {
14 | api.RegisterUserServant(e, newUserSrv())
15 | }
16 |
--------------------------------------------------------------------------------
/internal/servants/admin/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package admin
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/api/m/v1"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | var (
13 | _ api.User = (*userSrv)(nil)
14 | )
15 |
16 | type userSrv struct {
17 | *base.BaseServant
18 | api.UnimplementedUserServant
19 | }
20 |
21 | func newUserSrv() api.User {
22 | return &userSrv{
23 | BaseServant: base.NewBaseServant(),
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/servants/base/page.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package base
6 |
7 | type Pager struct {
8 | Page int `json:"page"`
9 | PageSize int `json:"page_size"`
10 | TotalRows int64 `json:"total_rows"`
11 | }
12 |
13 | type PageResp struct {
14 | List any `json:"list"`
15 | Pager Pager `json:"pager"`
16 | }
17 |
18 | func PageRespFrom(list any, page int, pageSize int, totalRows int64) *PageResp {
19 | return &PageResp{
20 | List: list,
21 | Pager: Pager{
22 | Page: page,
23 | PageSize: pageSize,
24 | TotalRows: totalRows,
25 | },
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/internal/servants/bot/bot.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package bot
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | api "github.com/rocboss/paopao-ce/auto/api/r/v1"
10 | )
11 |
12 | // RouteBot register Bot route
13 | func RouteBot(e *gin.Engine) {
14 | api.RegisterUserServant(e, newUserSrv())
15 | }
16 |
--------------------------------------------------------------------------------
/internal/servants/bot/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package bot
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/api/r/v1"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | var (
13 | _ api.User = (*userSrv)(nil)
14 | )
15 |
16 | type userSrv struct {
17 | *base.BaseServant
18 | api.UnimplementedUserServant
19 | }
20 |
21 | func newUserSrv() api.User {
22 | return &userSrv{
23 | BaseServant: base.NewBaseServant(),
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/servants/chain/admin.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | "github.com/rocboss/paopao-ce/internal/core/ms"
10 | "github.com/rocboss/paopao-ce/pkg/app"
11 | )
12 |
13 | func Admin() gin.HandlerFunc {
14 | return func(c *gin.Context) {
15 | if user, exist := c.Get("USER"); exist {
16 | if userModel, ok := user.(*ms.User); ok {
17 | if userModel.Status == ms.UserStatusNormal && userModel.IsAdmin {
18 | c.Next()
19 | return
20 | }
21 | }
22 | }
23 |
24 | response := app.NewResponse(c)
25 | response.ToErrorResponse(_errNoAdminPermission)
26 | c.Abort()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/internal/servants/chain/audit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | "github.com/rocboss/paopao-ce/internal/model/web"
10 | )
11 |
12 | func AuditHook() gin.HandlerFunc {
13 | return func(c *gin.Context) {
14 | // 此midleware后面是真正的http handlder,让handler先执行
15 | c.Next()
16 | // 审核hook 后处理逻辑
17 | var ami *web.AuditMetaInfo
18 | if val, ok := c.Get(web.AuditHookCtxKey); ok {
19 | if ami, ok = val.(*web.AuditMetaInfo); !ok {
20 | return
21 | }
22 | }
23 | OnAudiotHookEvent(ami)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/servants/chain/chain.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "sync"
9 |
10 | "github.com/rocboss/paopao-ce/internal/core"
11 | "github.com/rocboss/paopao-ce/internal/dao"
12 | "github.com/rocboss/paopao-ce/internal/dao/cache"
13 | )
14 |
15 | var (
16 | _ums core.UserManageService
17 | _ac core.AppCache
18 | _onceUms sync.Once
19 | )
20 |
21 | func userManageService() core.UserManageService {
22 | _onceUms.Do(func() {
23 | _ums = dao.DataService()
24 | _ac = cache.NewAppCache()
25 | })
26 | return _ums
27 | }
28 |
--------------------------------------------------------------------------------
/internal/servants/chain/events.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/alimy/tryst/event"
9 | "github.com/rocboss/paopao-ce/internal/infra/events"
10 | "github.com/rocboss/paopao-ce/internal/model/web"
11 | "github.com/sirupsen/logrus"
12 | )
13 |
14 | type AuditHookEvent struct {
15 | event.UnimplementedEvent
16 | ami *web.AuditMetaInfo
17 | }
18 |
19 | func (e *AuditHookEvent) Name() string {
20 | return "AuditHookEvent"
21 | }
22 |
23 | func (e *AuditHookEvent) Action() error {
24 | // TODO: just log event now, will add real logic in future.
25 | logrus.Debugf("auditHook event action style[%s] id[%d]", e.ami.Style, e.ami.Id)
26 | return nil
27 | }
28 |
29 | func OnAudiotHookEvent(ami *web.AuditMetaInfo) {
30 | if ami != nil {
31 | events.OnEvent(&AuditHookEvent{
32 | ami: ami,
33 | })
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/internal/servants/chain/measure.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | func OnlineUserMeasure() gin.HandlerFunc {
13 | return func(c *gin.Context) {
14 | // 此midleware后面是真正的http handlder,让handler先执行
15 | c.Next()
16 | // 更新用户在线状态
17 | if uid, ok := base.UserIdFrom(c); ok {
18 | OnUserOnlineMetric(_ac, uid)
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/internal/servants/chain/metrics.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/internal/conf"
9 | "github.com/rocboss/paopao-ce/internal/core"
10 | "github.com/rocboss/paopao-ce/internal/infra/metrics"
11 | )
12 |
13 | type OnlineUserMetric struct {
14 | metrics.BaseMetric
15 | ac core.AppCache
16 | uid int64
17 | expire int64
18 | }
19 |
20 | func OnUserOnlineMetric(ac core.AppCache, uid int64) {
21 | metrics.OnMeasure(&OnlineUserMetric{
22 | ac: ac,
23 | uid: uid,
24 | expire: conf.CacheSetting.OnlineUserExpire,
25 | })
26 | }
27 |
28 | func (m *OnlineUserMetric) Name() string {
29 | return "OnlineUserMetric"
30 | }
31 |
32 | func (m *OnlineUserMetric) Action() (err error) {
33 | // 暂时仅做标记,不存储其他相关信息
34 | m.ac.SetNx(conf.KeyOnlineUser.Get(m.uid), []byte{}, m.expire)
35 | return
36 | }
37 |
--------------------------------------------------------------------------------
/internal/servants/chain/xerror.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package chain
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/pkg/xerror"
9 | )
10 |
11 | // nolint
12 | var (
13 | _errUserHasBeenBanned = xerror.NewError(20006, "该账户已被封停")
14 | _errAccountNoPhoneBind = xerror.NewError(20013, "拒绝操作: 账户未绑定手机号")
15 | _errNoAdminPermission = xerror.NewError(20022, "无管理权限")
16 | )
17 |
--------------------------------------------------------------------------------
/internal/servants/docs/docs.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !docs
6 | // +build !docs
7 |
8 | package docs
9 |
10 | import (
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | // RegisterDocs stub function for register docs asset route
15 | func RegisterDocs(e *gin.Engine) {
16 | // empty
17 | }
18 |
--------------------------------------------------------------------------------
/internal/servants/docs/docs_embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build docs
6 | // +build docs
7 |
8 | package docs
9 |
10 | import (
11 | "github.com/alimy/tryst/cfg"
12 | "github.com/gin-gonic/gin"
13 | "github.com/rocboss/paopao-ce/docs/openapi"
14 | )
15 |
16 | // RegisterDocs register docs asset route
17 | func RegisterDocs(e *gin.Engine) {
18 | cfg.Be("Docs:OpenAPI", func() {
19 | e.StaticFS("/docs/openapi", openapi.NewFileSystem())
20 | })
21 | }
22 |
--------------------------------------------------------------------------------
/internal/servants/localoss/localoss.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package localoss
6 |
7 | import (
8 | "path/filepath"
9 |
10 | "github.com/gin-gonic/gin"
11 | api "github.com/rocboss/paopao-ce/auto/api/s/v1"
12 | "github.com/rocboss/paopao-ce/internal/conf"
13 | "github.com/sirupsen/logrus"
14 | )
15 |
16 | // RouteLocalOSS register LocalOSS route if needed
17 | func RouteLocalOSS(e *gin.Engine) {
18 | savePath, err := filepath.Abs(conf.LocalOSSSetting.SavePath)
19 | if err != nil {
20 | logrus.Fatalf("get localOSS save path err: %v", err)
21 | }
22 | e.Static("/oss", savePath)
23 |
24 | logrus.Infof("register LocalOSS route in /oss on save path: %s", savePath)
25 | }
26 |
27 | // RouteLocaloss register LocalOSS route if needed
28 | func RouteLocaloss(e *gin.Engine) {
29 | api.RegisterUserServant(e, newUserSrv())
30 | }
31 |
--------------------------------------------------------------------------------
/internal/servants/localoss/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package localoss
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/api/s/v1"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | var (
13 | _ api.User = (*userSrv)(nil)
14 | )
15 |
16 | type userSrv struct {
17 | *base.BaseServant
18 | api.UnimplementedUserServant
19 | }
20 |
21 | func newUserSrv() api.User {
22 | return &userSrv{
23 | BaseServant: base.NewBaseServant(),
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/servants/mobile/greet.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package mobile
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/rpc/greet/v1"
9 | )
10 |
11 | var (
12 | _ api.GreetServiceServer = (*greetServiceSrv)(nil)
13 | )
14 |
15 | type greetServiceSrv struct {
16 | api.UnimplementedGreetServiceServer
17 | }
18 |
19 | func newGreetServiceServer() *greetServiceSrv {
20 | return &greetServiceSrv{}
21 | }
22 |
--------------------------------------------------------------------------------
/internal/servants/mobile/mobile.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package mobile
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/rpc/greet/v1"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | func RegisterServants(s *grpc.Server) {
13 | api.RegisterGreetServiceServer(s, newGreetServiceServer())
14 | }
15 |
--------------------------------------------------------------------------------
/internal/servants/space/space.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package space
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | api "github.com/rocboss/paopao-ce/auto/api/x/v1"
10 | )
11 |
12 | // RouteWeb register SpaceX route
13 | func RouteSpaceX(e *gin.Engine) {
14 | api.RegisterUserServant(e, newUserSrv())
15 | }
16 |
--------------------------------------------------------------------------------
/internal/servants/space/user.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package space
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/api/x/v1"
9 | "github.com/rocboss/paopao-ce/internal/servants/base"
10 | )
11 |
12 | var (
13 | _ api.User = (*userSrv)(nil)
14 | )
15 |
16 | type userSrv struct {
17 | *base.BaseServant
18 | api.UnimplementedUserServant
19 | }
20 |
21 | func newUserSrv() api.User {
22 | return &userSrv{
23 | BaseServant: base.NewBaseServant(),
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/internal/servants/statick/statick.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build slim && embed
6 | // +build slim,embed
7 |
8 | package statick
9 |
10 | import (
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | // RegisterWebStatick stub function for register static asset route
15 | func RegisterWebStatick(e *gin.Engine) {
16 | // empty
17 | }
18 |
--------------------------------------------------------------------------------
/internal/servants/statick/statick_embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !(slim && embed)
6 | // +build !slim !embed
7 |
8 | package statick
9 |
10 | import (
11 | "net/http"
12 |
13 | "github.com/gin-gonic/gin"
14 | "github.com/rocboss/paopao-ce/web"
15 | )
16 |
17 | // RegisterWebStatick register web static assets route
18 | func RegisterWebStatick(e *gin.Engine) {
19 | routeWebStatic(e, "/", "/index.html", "/favicon.ico", "/logo.png", "/sw.js", "/manifest.json", "/assets/*filepath")
20 | }
21 |
22 | func routeWebStatic(e *gin.Engine, paths ...string) {
23 | staticHandler := http.FileServer(web.NewFileSystem())
24 | handler := func(c *gin.Context) {
25 | staticHandler.ServeHTTP(c.Writer, c.Request)
26 | }
27 | for _, path := range paths {
28 | e.GET(path, handler)
29 | e.HEAD(path, handler)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/internal/servants/web/assets/assets.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package assets
6 |
7 | import (
8 | _ "embed"
9 | )
10 |
11 | //go:embed comic.ttf
12 | var ComicBytes []byte
13 |
--------------------------------------------------------------------------------
/internal/servants/web/assets/comic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/internal/servants/web/assets/comic.ttf
--------------------------------------------------------------------------------
/internal/servants/web/site.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package web
6 |
7 | import (
8 | api "github.com/rocboss/paopao-ce/auto/api/v1"
9 | "github.com/rocboss/paopao-ce/internal/conf"
10 | "github.com/rocboss/paopao-ce/internal/model/web"
11 | "github.com/rocboss/paopao-ce/internal/servants/base"
12 | "github.com/rocboss/paopao-ce/pkg/version"
13 | )
14 |
15 | var (
16 | _ api.Site = (*siteSrv)(nil)
17 | )
18 |
19 | type siteSrv struct {
20 | api.UnimplementedSiteServant
21 | *base.BaseServant
22 | }
23 |
24 | func (*siteSrv) Profile() (*web.SiteProfileResp, error) {
25 | return conf.WebProfileSetting, nil
26 | }
27 |
28 | func (*siteSrv) Version() (*web.VersionResp, error) {
29 | return &web.VersionResp{
30 | BuildInfo: version.ReadBuildInfo(),
31 | }, nil
32 | }
33 |
34 | func newSiteSrv() api.Site {
35 | return &siteSrv{
36 | BaseServant: base.NewBaseServant(),
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/internal/service/grpc_server.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package service
6 |
7 | import (
8 | "net"
9 |
10 | "google.golang.org/grpc"
11 | )
12 |
13 | var (
14 | _ server = (*grpcServer)(nil)
15 | )
16 |
17 | // grpcServer wraper for grpc.Server
18 | type grpcServer struct {
19 | *baseServer
20 |
21 | listener net.Listener
22 | server *grpc.Server
23 | }
24 |
25 | func (s *grpcServer) start() error {
26 | return s.server.Serve(s.listener)
27 | }
28 |
29 | func (s *grpcServer) stop() error {
30 | s.server.Stop()
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/internal/service/grpc_service.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package service
6 |
7 | import (
8 | "google.golang.org/grpc"
9 | )
10 |
11 | type baseGRPCService struct {
12 | baseService
13 |
14 | server *grpcServer
15 | }
16 |
17 | func (s *baseGRPCService) registerServer(srv Service, h func(s *grpc.Server)) {
18 | h(s.server.server)
19 | s.server.addService(srv)
20 | }
21 |
22 | func (s *baseGRPCService) OnStart() error {
23 | // do nothing default
24 | return nil
25 | }
26 |
27 | func (s *baseGRPCService) OnStop() error {
28 | // do nothing default
29 | return nil
30 | }
31 |
--------------------------------------------------------------------------------
/internal/service/http_server.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package service
6 |
7 | import (
8 | "context"
9 | "net/http"
10 |
11 | "github.com/gin-gonic/gin"
12 | )
13 |
14 | var (
15 | _ server = (*httpServer)(nil)
16 | )
17 |
18 | // httpServer wraper for gin.engine and http.Server
19 | type httpServer struct {
20 | *baseServer
21 |
22 | e *gin.Engine
23 | server *http.Server
24 | }
25 |
26 | func (s *httpServer) start() error {
27 | return s.server.ListenAndServe()
28 | }
29 |
30 | func (s *httpServer) stop() error {
31 | return s.server.Shutdown(context.Background())
32 | }
33 |
--------------------------------------------------------------------------------
/internal/service/http_service.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package service
6 |
7 | import (
8 | "github.com/gin-gonic/gin"
9 | )
10 |
11 | type baseHttpService struct {
12 | baseService
13 |
14 | server *httpServer
15 | }
16 |
17 | func (s *baseHttpService) registerRoute(srv Service, h func(e *gin.Engine)) {
18 | if h != nil {
19 | h(s.server.e)
20 | }
21 | s.server.addService(srv)
22 | }
23 |
24 | func (s *baseHttpService) OnStart() error {
25 | // do nothing default
26 | return nil
27 | }
28 |
29 | func (s *baseHttpService) OnStop() error {
30 | // do nothing default
31 | return nil
32 | }
33 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package main
6 |
7 | import (
8 | "github.com/rocboss/paopao-ce/cmd"
9 | _ "github.com/rocboss/paopao-ce/cmd/migrate"
10 | _ "github.com/rocboss/paopao-ce/cmd/serve"
11 | )
12 |
13 | func main() {
14 | cmd.Execute()
15 | }
16 |
--------------------------------------------------------------------------------
/mirc/README.md:
--------------------------------------------------------------------------------
1 | ### RESTful API for paopao-ce
2 | 本目录包含所有RESTful API相关定义文件
3 |
4 | |服务|目录|系列API| url前缀|备注|
5 | | ----- | ----- | ----- | ----- | ----- |
6 | |Web|web|/|/|Web系列RESTful API相关定义文件|
7 | |Admin|admin|m|/m/|Admin后台运维系列相关RESTful API相关定义文件|
8 | |SpaceX|space|x|/x/|SpaceX系列相关RESTful API相关定义文件|
9 | |NativeOBS|localoss|s|/s/| NativeOBS系列RESTful API相关定义文件|
10 | |Bot|bot|r| /r/|Bot系列相关RESTful API相关定义文件|
11 |
--------------------------------------------------------------------------------
/mirc/admin/README.md:
--------------------------------------------------------------------------------
1 | ### Admin系列RESTful API相关定义文件
2 | 本目录包含 Admin后台运维相关API定义文件。
3 |
4 | * v1 - v1版本API
5 |
--------------------------------------------------------------------------------
/mirc/admin/v1/user.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 | )
6 |
7 | type AgentInfo struct {
8 | Platform string `json:"platform"`
9 | UserAgent string `json:"user_agent"`
10 | }
11 |
12 | type ServerInfo struct {
13 | ApiVer string `json:"api_ver"`
14 | }
15 |
16 | type UserInfo struct {
17 | Name string `json:"name"`
18 | }
19 |
20 | type LoginReq struct {
21 | AgentInfo AgentInfo `json:"agent_info"`
22 | Name string `json:"name"`
23 | Passwd string `json:"passwd"`
24 | }
25 |
26 | type LoginResp struct {
27 | UserInfo
28 | ServerInfo ServerInfo `json:"server_info"`
29 | JwtToken string `json:"jwt_token"`
30 | }
31 |
32 | type User struct {
33 | Schema `mir:"m/v1,chain"`
34 |
35 | Login func(Post, LoginReq) LoginResp `mir:"user/login"`
36 | Logout func(Post) `mir:"user/logout"`
37 | }
38 |
--------------------------------------------------------------------------------
/mirc/bot/README.md:
--------------------------------------------------------------------------------
1 | ### Bot系列RESTful API相关定义文件
2 | 本目录包含 Bot相关API定义文件。
3 |
4 | * v1 - v1版本API
5 |
--------------------------------------------------------------------------------
/mirc/bot/v1/user.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 | )
6 |
7 | type AgentInfo struct {
8 | Platform string `json:"platform"`
9 | UserAgent string `json:"user_agent"`
10 | }
11 |
12 | type ServerInfo struct {
13 | ApiVer string `json:"api_ver"`
14 | }
15 |
16 | type UserInfo struct {
17 | Name string `json:"name"`
18 | }
19 |
20 | type LoginReq struct {
21 | AgentInfo AgentInfo `json:"agent_info"`
22 | Name string `json:"name"`
23 | Passwd string `json:"passwd"`
24 | }
25 |
26 | type LoginResp struct {
27 | UserInfo
28 | ServerInfo ServerInfo `json:"server_info"`
29 | JwtToken string `json:"jwt_token"`
30 | }
31 |
32 | type User struct {
33 | Schema `mir:"r/v1,chain"`
34 |
35 | Login func(Post, LoginReq) LoginResp `mir:"user/login"`
36 | Logout func(Post) `mir:"user/logout"`
37 | }
38 |
--------------------------------------------------------------------------------
/mirc/gen.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build generate
6 | // +build generate
7 |
8 | package main
9 |
10 | import (
11 | "log"
12 |
13 | . "github.com/alimy/mir/v5/core"
14 | . "github.com/alimy/mir/v5/engine"
15 | )
16 |
17 | //go:generate go run $GOFILE
18 | func main() {
19 | log.Println("[Mir] generate code start")
20 | if err := Generate(
21 | UseGin(),
22 | Schema("web", "space", "localoss", "bot", "admin"),
23 | SinkPath("../auto"),
24 | WatchCtxDone(true),
25 | RunMode(InSerialMode),
26 | ); err != nil {
27 | log.Fatal(err)
28 | }
29 | log.Println("[Mir] generate code finish")
30 | }
31 |
--------------------------------------------------------------------------------
/mirc/localoss/README.md:
--------------------------------------------------------------------------------
1 | ### LocalOSS OBS系列RESTful API相关定义文件
2 | 本目录包含 LocalOSS OBS 系列相关API定义文件。
3 |
4 | * v1 - v1版本API
5 |
--------------------------------------------------------------------------------
/mirc/localoss/v1/user.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 | )
6 |
7 | type AgentInfo struct {
8 | Platform string `json:"platform"`
9 | UserAgent string `json:"user_agent"`
10 | }
11 |
12 | type ServerInfo struct {
13 | ApiVer string `json:"api_ver"`
14 | }
15 |
16 | type UserInfo struct {
17 | Name string `json:"name"`
18 | }
19 |
20 | type LoginReq struct {
21 | AgentInfo AgentInfo `json:"agent_info"`
22 | Name string `json:"name"`
23 | Passwd string `json:"passwd"`
24 | }
25 |
26 | type LoginResp struct {
27 | UserInfo
28 | ServerInfo ServerInfo `json:"server_info"`
29 | JwtToken string `json:"jwt_token"`
30 | }
31 |
32 | type User struct {
33 | Schema `mir:"s/v1"`
34 |
35 | Index func(Get) `mir:"index"`
36 | Login func(Post, LoginReq) LoginResp `mir:"user/login"`
37 | Logout func(Post) `mir:"user/logout"`
38 | }
39 |
--------------------------------------------------------------------------------
/mirc/space/README.md:
--------------------------------------------------------------------------------
1 | ### SpaceX系列RESTful API
2 | 本目录包含SpaceX系列RESTful API相关定义文件
3 |
4 | * v1 - v1版本API
5 |
--------------------------------------------------------------------------------
/mirc/space/v1/user.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 | )
6 |
7 | type AgentInfo struct {
8 | Platform string `json:"platform"`
9 | UserAgent string `json:"user_agent"`
10 | }
11 |
12 | type ServerInfo struct {
13 | ApiVer string `json:"api_ver"`
14 | }
15 |
16 | type UserInfo struct {
17 | Name string `json:"name"`
18 | }
19 |
20 | type LoginReq struct {
21 | AgentInfo AgentInfo `json:"agent_info"`
22 | Name string `json:"name"`
23 | Passwd string `json:"passwd"`
24 | }
25 |
26 | type LoginResp struct {
27 | UserInfo
28 | ServerInfo ServerInfo `json:"server_info"`
29 | JwtToken string `json:"jwt_token"`
30 | }
31 |
32 | type User struct {
33 | Schema `mir:"x/v1,chain"`
34 |
35 | Login func(Post, LoginReq) LoginResp `mir:"user/login"`
36 | Logout func(Post) `mir:"user/logout"`
37 | }
38 |
--------------------------------------------------------------------------------
/mirc/web/README.md:
--------------------------------------------------------------------------------
1 | ### Web系列RESTful API
2 | 本目录包含Web系列RESTful API相关定义文件
3 |
4 | * v1 - v1版本API
5 |
--------------------------------------------------------------------------------
/mirc/web/v1/admin.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Admin 运维相关服务
10 | type Admin struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // ChangeUserStatus 管理·禁言/解封用户
14 | ChangeUserStatus func(Post, web.ChangeUserStatusReq) `mir:"admin/user/status"`
15 | SiteInfo func(Get, web.SiteInfoReq) web.SiteInfoResp `mir:"admin/site/status"`
16 | }
17 |
--------------------------------------------------------------------------------
/mirc/web/v1/alipay.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // AlipayPub 支付宝相关不用授权的服务
10 | type AlipayPub struct {
11 | Schema `mir:"v1"`
12 |
13 | // AlipayNotify 支付宝回调
14 | AlipayNotify func(Post, web.AlipayNotifyReq) `mir:"alipay/notify"`
15 | }
16 |
17 | // AlipayPub 支付宝相关授权的服务
18 | type AlipayPriv struct {
19 | Schema `mir:"v1,chain"`
20 |
21 | // UserRechargeLink 用户充值
22 | UserRechargeLink func(Post, web.UserRechargeLinkReq) web.UserRechargeLinkResp `mir:"user/recharge"`
23 |
24 | // UserRechargeResult 获取充值结果
25 | UserRechargeResult func(Get, web.UserRechargeResultReq) web.UserRechargeResultResp `mir:"user/recharge"`
26 |
27 | // UserWalletBills 获取用户账单
28 | UserWalletBills func(Get, web.UserWalletBillsReq) web.UserWalletBillsResp `mir:"user/wallet/bills"`
29 | }
30 |
--------------------------------------------------------------------------------
/mirc/web/v1/followship.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Followship 关注者模式 服务
10 | type Followship struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // FollowUser 关注用户
14 | FollowUser func(Post, web.FollowUserReq) `mir:"user/follow"`
15 |
16 | // UnfollowUser 取消关注用户
17 | UnfollowUser func(Post, web.UnfollowUserReq) `mir:"user/unfollow"`
18 |
19 | // ListFollows 获取用户的关注列表
20 | ListFollows func(Get, web.ListFollowsReq) web.ListFollowsResp `mir:"user/follows"`
21 |
22 | // ListFollowings 获取用户的追随者列表
23 | ListFollowings func(Get, web.ListFollowingsReq) web.ListFollowingsResp `mir:"user/followings"`
24 | }
25 |
--------------------------------------------------------------------------------
/mirc/web/v1/friendship.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Friendship 好友模式 服务
10 | type Friendship struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // RequestingFriend 请求添加朋友
14 | RequestingFriend func(Post, web.RequestingFriendReq) `mir:"friend/requesting" binding:"json"`
15 |
16 | // AddFriend 同意添加好友
17 | AddFriend func(Post, web.AddFriendReq) `mir:"friend/add" binding:"json"`
18 |
19 | // RejectFriend 拒绝添加好友
20 | RejectFriend func(Post, web.RejectFriendReq) `mir:"friend/reject" binding:"json"`
21 |
22 | // DeleteFriend 删除好友
23 | DeleteFriend func(Post, web.DeleteFriendReq) `mir:"friend/delete" binding:"json"`
24 |
25 | // GetContacts 获取好友列表
26 | GetContacts func(Get, web.GetContactsReq) web.GetContactsResp `mir:"user/contacts"`
27 | }
28 |
--------------------------------------------------------------------------------
/mirc/web/v1/loose.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Loose 宽松授权的服务
10 | type Loose struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // Timeline 获取广场流
14 | Timeline func(Get, web.TimelineReq) web.TimelineResp `mir:"posts"`
15 |
16 | // GetUserTweets 获取用户动态列表
17 | GetUserTweets func(Get, web.GetUserTweetsReq) web.GetUserTweetsResp `mir:"user/posts"`
18 |
19 | // GetUserProfile 获取用户基本信息
20 | GetUserProfile func(Get, web.GetUserProfileReq) web.GetUserProfileResp `mir:"user/profile"`
21 |
22 | // TopicList 获取话题列表
23 | TopicList func(Get, web.TopicListReq) web.TopicListResp `mir:"tags"`
24 |
25 | // TweetComments 获取动态评论
26 | TweetComments func(Get, web.TweetCommentsReq) web.TweetCommentsResp `mir:"post/comments"`
27 |
28 | // TweetDetail 获取动态详情
29 | TweetDetail func(Get, web.TweetDetailReq) web.TweetDetailResp `mir:"post"`
30 | }
31 |
--------------------------------------------------------------------------------
/mirc/web/v1/pub.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Pub 不用授权的公开服务
10 | type Pub struct {
11 | Schema `mir:"v1"`
12 |
13 | // Version 获取后台版本信息
14 | Version func(Get) web.VersionResp `mir:"/"`
15 |
16 | // Login 用户登录
17 | Login func(Post, web.LoginReq) web.LoginResp `mir:"/auth/login"`
18 |
19 | // Register 用户注册
20 | Register func(Post, web.RegisterReq) web.RegisterResp `mir:"/auth/register"`
21 |
22 | // GetCaptcha 获取验证码
23 | GetCaptcha func(Get) web.GetCaptchaResp `mir:"/captcha"`
24 |
25 | // SendCaptcha 发送验证码
26 | SendCaptcha func(Post, web.SendCaptchaReq) `mir:"/captcha"`
27 | }
28 |
--------------------------------------------------------------------------------
/mirc/web/v1/relax.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Relax 放宽授权的服务
10 | type Relax struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // GetUnreadMsgCount 获取当前用户未读消息数量
14 | GetUnreadMsgCount func(Get, Chain, web.GetUnreadMsgCountReq) web.GetUnreadMsgCountResp `mir:"user/msgcount/unread"`
15 | }
16 |
--------------------------------------------------------------------------------
/mirc/web/v1/site.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Site 站点本身相关的信息服务
10 | type Site struct {
11 | Schema `mir:"v1"`
12 |
13 | // Version 获取后台版本信息
14 | Version func(Get) web.VersionResp `mir:"site/version"`
15 |
16 | // Profile 站点配置概要信息
17 | Profile func(Get) web.SiteProfileResp `mir:"site/profile"`
18 | }
19 |
--------------------------------------------------------------------------------
/mirc/web/v1/trends.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | . "github.com/alimy/mir/v5"
5 |
6 | "github.com/rocboss/paopao-ce/internal/model/web"
7 | )
8 |
9 | // Trends 动态相关 服务
10 | type Trends struct {
11 | Schema `mir:"v1,chain"`
12 |
13 | // GetIndexTrends 获取广场页面动态条栏的索引item
14 | GetIndexTrends func(Get, web.GetIndexTrendsReq) web.GetIndexTrendsResp `mir:"trends/index"`
15 | }
16 |
--------------------------------------------------------------------------------
/pkg/app/form.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package app
6 |
7 | import (
8 | "strings"
9 |
10 | "github.com/gin-gonic/gin"
11 | )
12 |
13 | type ValidError struct {
14 | Message string
15 | }
16 |
17 | type ValidErrors []*ValidError
18 |
19 | func (v *ValidError) Error() string {
20 | return v.Message
21 | }
22 |
23 | func (v ValidErrors) Error() string {
24 | return strings.Join(v.Errors(), ",")
25 | }
26 |
27 | func (v ValidErrors) Errors() []string {
28 | var errs []string
29 | for _, err := range v {
30 | errs = append(errs, err.Error())
31 | }
32 |
33 | return errs
34 | }
35 |
36 | func BindAndValid(c *gin.Context, v any) (bool, ValidErrors) {
37 | var errs ValidErrors
38 | err := c.ShouldBind(v)
39 | if err != nil {
40 | errs = append(errs, &ValidError{
41 | Message: err.Error(),
42 | })
43 |
44 | return false, errs
45 | }
46 |
47 | return true, nil
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/convert/convert.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package convert
6 |
7 | import "strconv"
8 |
9 | type StrTo string
10 |
11 | func (s StrTo) String() string {
12 | return string(s)
13 | }
14 |
15 | func (s StrTo) Int() (int, error) {
16 | v, err := strconv.Atoi(s.String())
17 | return v, err
18 | }
19 |
20 | func (s StrTo) MustInt() int {
21 | v, _ := s.Int()
22 | return v
23 | }
24 |
25 | func (s StrTo) UInt32() (uint32, error) {
26 | v, err := strconv.Atoi(s.String())
27 | return uint32(v), err
28 | }
29 |
30 | func (s StrTo) MustUInt32() uint32 {
31 | v, _ := s.UInt32()
32 | return v
33 | }
34 |
35 | func (s StrTo) Int64() (int64, error) {
36 | v, err := strconv.ParseInt(s.String(), 10, 64)
37 | return v, err
38 | }
39 |
40 | func (s StrTo) MustInt64() int64 {
41 | v, _ := s.Int64()
42 | return v
43 | }
44 |
45 | func (s StrTo) Float64() (float64, error) {
46 | return strconv.ParseFloat(s.String(), 64)
47 | }
48 |
49 | func (s StrTo) MustFloat64() float64 {
50 | v, _ := strconv.ParseFloat(s.String(), 64)
51 | return v
52 | }
53 |
--------------------------------------------------------------------------------
/pkg/convert/convert_suite_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package convert_test
6 |
7 | import (
8 | "testing"
9 |
10 | . "github.com/onsi/ginkgo/v2"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func TestConvert(t *testing.T) {
15 | RegisterFailHandler(Fail)
16 | RunSpecs(t, "Convert Suite")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/debug/annotation.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package debug
6 |
7 | import (
8 | "github.com/sirupsen/logrus"
9 | )
10 |
11 | func TODO() {
12 | logrus.Fatalln("in todo progress")
13 | }
14 |
15 | func NotImplemented() {
16 | logrus.Fatalln("not implemented")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/debug/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package debug
6 |
7 | import (
8 | "errors"
9 | )
10 |
11 | var (
12 | ErrNotImplemented = errors.New("not implemented")
13 | )
14 |
--------------------------------------------------------------------------------
/pkg/debug/pprof_embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build pprof
6 | // +build pprof
7 |
8 | package debug
9 |
10 | import (
11 | _ "net/http/pprof"
12 | )
13 |
--------------------------------------------------------------------------------
/pkg/debug/pyroscope.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !pyroscope
6 | // +build !pyroscope
7 |
8 | package debug
9 |
10 | import (
11 | "github.com/alimy/tryst/cfg"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func StartPyroscope() {
16 | if cfg.If("Pyroscope") {
17 | logrus.Infoln("want Pyroscope feature but not support in this compile version")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/http/http.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | // package http contain some custom help function for std http library.
6 |
7 | package http
8 |
--------------------------------------------------------------------------------
/pkg/http/http_suite_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package http_test
6 |
7 | import (
8 | "testing"
9 |
10 | . "github.com/onsi/ginkgo/v2"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func TestHttp(t *testing.T) {
15 | RegisterFailHandler(Fail)
16 | RunSpecs(t, "Http Suite")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/json/go_json.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Bo-Yi Wu. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build go_json
6 | // +build go_json
7 |
8 | package json
9 |
10 | import json "github.com/goccy/go-json"
11 |
12 | var (
13 | // Marshal is exported by gin/json package.
14 | Marshal = json.Marshal
15 | // Unmarshal is exported by gin/json package.
16 | Unmarshal = json.Unmarshal
17 | // MarshalIndent is exported by gin/json package.
18 | MarshalIndent = json.MarshalIndent
19 | // NewDecoder is exported by gin/json package.
20 | NewDecoder = json.NewDecoder
21 | // NewEncoder is exported by gin/json package.
22 | NewEncoder = json.NewEncoder
23 | )
24 |
--------------------------------------------------------------------------------
/pkg/json/json.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Bo-Yi Wu. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !jsoniter && !go_json && !(sonic && avx && (linux || windows || darwin) && amd64)
6 | // +build !jsoniter
7 | // +build !go_json
8 | // +build !sonic !avx !linux,!windows,!darwin !amd64
9 |
10 | package json
11 |
12 | import "encoding/json"
13 |
14 | var (
15 | // Marshal is exported by gin/json package.
16 | Marshal = json.Marshal
17 | // Unmarshal is exported by gin/json package.
18 | Unmarshal = json.Unmarshal
19 | // MarshalIndent is exported by gin/json package.
20 | MarshalIndent = json.MarshalIndent
21 | // NewDecoder is exported by gin/json package.
22 | NewDecoder = json.NewDecoder
23 | // NewEncoder is exported by gin/json package.
24 | NewEncoder = json.NewEncoder
25 | )
26 |
--------------------------------------------------------------------------------
/pkg/json/jsoniter.go:
--------------------------------------------------------------------------------
1 | // Copyright 2017 Bo-Yi Wu. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build jsoniter
6 | // +build jsoniter
7 |
8 | package json
9 |
10 | import jsoniter "github.com/json-iterator/go"
11 |
12 | var (
13 | json = jsoniter.ConfigCompatibleWithStandardLibrary
14 | // Marshal is exported by gin/json package.
15 | Marshal = json.Marshal
16 | // Unmarshal is exported by gin/json package.
17 | Unmarshal = json.Unmarshal
18 | // MarshalIndent is exported by gin/json package.
19 | MarshalIndent = json.MarshalIndent
20 | // NewDecoder is exported by gin/json package.
21 | NewDecoder = json.NewDecoder
22 | // NewEncoder is exported by gin/json package.
23 | NewEncoder = json.NewEncoder
24 | )
25 |
--------------------------------------------------------------------------------
/pkg/json/sonic.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 Gin Core Team. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build sonic && avx && (linux || windows || darwin) && amd64
6 | // +build sonic
7 | // +build avx
8 | // +build linux windows darwin
9 | // +build amd64
10 |
11 | package json
12 |
13 | import "github.com/bytedance/sonic"
14 |
15 | var (
16 | json = sonic.ConfigStd
17 | // Marshal is exported by gin/json package.
18 | Marshal = json.Marshal
19 | // Unmarshal is exported by gin/json package.
20 | Unmarshal = json.Unmarshal
21 | // MarshalIndent is exported by gin/json package.
22 | MarshalIndent = json.MarshalIndent
23 | // NewDecoder is exported by gin/json package.
24 | NewDecoder = json.NewDecoder
25 | // NewEncoder is exported by gin/json package.
26 | NewEncoder = json.NewEncoder
27 | )
28 |
--------------------------------------------------------------------------------
/pkg/naming/naming.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Michael Li . All rights reserved.
2 | // Use of this source code is governed by Apache License 2.0 that
3 | // can be found in the LICENSE file.
4 |
5 | package naming
6 |
7 | // NamingStrategy naming strategy interface
8 | type NamingStrategy interface {
9 | Naming(string) string
10 | }
11 |
--------------------------------------------------------------------------------
/pkg/naming/simple_ns.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 Michael Li . All rights reserved.
2 | // Use of this source code is governed by Apache License 2.0 that
3 | // can be found in the LICENSE file.
4 |
5 | package naming
6 |
7 | import (
8 | "strings"
9 | "unicode"
10 | )
11 |
12 | type simpleNamingStrategy struct {
13 | // just empty
14 | }
15 |
16 | func (s *simpleNamingStrategy) Naming(name string) string {
17 | b := &strings.Builder{}
18 | notFirst := false
19 | b.Grow(len(name) + 3)
20 | for _, r := range name {
21 | if unicode.IsUpper(r) {
22 | if notFirst {
23 | b.WriteRune('_')
24 | }
25 | b.WriteRune(unicode.ToLower(r))
26 | } else {
27 | b.WriteRune(r)
28 | }
29 | notFirst = true
30 | }
31 | return b.String()
32 | }
33 |
34 | // NewSimpleNamingStrategy return simple naming strategy instance
35 | func NewSimpleNamingStrategy() NamingStrategy {
36 | return &simpleNamingStrategy{}
37 | }
38 |
--------------------------------------------------------------------------------
/pkg/types/bitmap_roaring_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package types_test
6 |
7 | import (
8 | . "github.com/onsi/ginkgo/v2"
9 | . "github.com/onsi/gomega"
10 |
11 | "github.com/rocboss/paopao-ce/pkg/types"
12 | )
13 |
14 | var _ = Describe("Bitmap", Ordered, func() {
15 | It("sql scaner for Bitmap", func() {
16 | bitmap := types.NewBitmap()
17 | bitmap.Data.Map.Add(128)
18 | value, err := bitmap.Value()
19 | Expect(err).To(BeNil())
20 |
21 | var bm types.Bitmap
22 | bm.Scan(value)
23 | Expect(bm.Data.Map.Contains(128)).To(BeTrue())
24 | })
25 |
26 | It("sql scaner for Bitmap64", func() {
27 | bitmap := types.NewBitmap64()
28 | bitmap.Data.Map.Add(128)
29 | value, err := bitmap.Value()
30 | Expect(err).To(BeNil())
31 |
32 | var bm types.Bitmap64
33 | bm.Scan(value)
34 | Expect(bm.Data.Map.Contains(128)).To(BeTrue())
35 | })
36 | })
37 |
--------------------------------------------------------------------------------
/pkg/types/io.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package types
6 |
7 | import (
8 | "io"
9 | )
10 |
11 | type readerWrap struct {
12 | r io.Reader
13 | }
14 |
15 | func (rw *readerWrap) Read(p []byte) (n int, err error) {
16 | return rw.r.Read(p)
17 | }
18 |
19 | // PureReader wrap a pure io.Reader object
20 | func PureReader(r io.Reader) io.Reader {
21 | return &readerWrap{
22 | r: r,
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/types/password.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package types
6 |
7 | import (
8 | "golang.org/x/crypto/bcrypt"
9 | )
10 |
11 | type PasswordProvider interface {
12 | Generate(password []byte) ([]byte, error)
13 | Compare(hashedPassword, password []byte) error
14 | }
15 |
16 | func NewBcryptPasswordProvider(cost int) PasswordProvider {
17 | return &bcryptPasswordProvider{
18 | cost: cost,
19 | }
20 | }
21 |
22 | type bcryptPasswordProvider struct {
23 | cost int
24 | }
25 |
26 | func (p *bcryptPasswordProvider) Generate(password []byte) ([]byte, error) {
27 | return bcrypt.GenerateFromPassword(password, p.cost)
28 | }
29 |
30 | func (p *bcryptPasswordProvider) Compare(hashedPassword, password []byte) error {
31 | return bcrypt.CompareHashAndPassword(hashedPassword, password)
32 | }
33 |
--------------------------------------------------------------------------------
/pkg/types/types.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package types
6 |
7 | const (
8 | // No 二态值 否
9 | No int8 = 0
10 |
11 | // Yes 二态值 是
12 | Yes int8 = 1
13 | )
14 |
15 | // Empty empty alias type
16 | type Empty = struct{}
17 |
18 | // Fn empty argument func alias type
19 | type Fn = func()
20 |
21 | // Boxes Box/Unbox interface
22 | type Boxes[T any] interface {
23 | Box(t T)
24 | Unbox() T
25 | }
26 |
27 | type Integer interface {
28 | ~int8 | ~int16 | ~int32 | ~int64 | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~int | ~uint
29 | }
30 |
--------------------------------------------------------------------------------
/pkg/types/types_suite_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package types_test
6 |
7 | import (
8 | "testing"
9 |
10 | . "github.com/onsi/ginkgo/v2"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func TestTypes(t *testing.T) {
15 | RegisterFailHandler(Fail)
16 | RunSpecs(t, "Types Suite")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/utils/banner.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils
6 |
7 | import (
8 | "fmt"
9 |
10 | "github.com/Masterminds/semver/v3"
11 | )
12 |
13 | func PrintHelloBanner(text string) {
14 |
15 | fmt.Println(" ____ __ __ ____ __ __ ")
16 | fmt.Println("( _ \\ / _\\ / \\( _ \\ / _\\ / \\ ")
17 | fmt.Println(" ) __// \\( O )) __// \\( O )")
18 | fmt.Println("(__) \\_/\\_/ \\__/(__) \\_/\\_/ \\__/ ")
19 | fmt.Println(text)
20 | }
21 |
22 | func SidStr(name string, version *semver.Version, size int) string {
23 | return fmt.Sprintf(fmt.Sprintf("%%s@%%-%ds", size-len(name+version.String())+4), name, version)
24 | }
25 |
--------------------------------------------------------------------------------
/pkg/utils/ip.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils
6 |
7 | import "github.com/rocboss/paopao-ce/pkg/utils/iploc"
8 |
9 | func GetIPLoc(ip string) string {
10 | country, _ := iploc.Find(ip)
11 | return country
12 | }
13 |
--------------------------------------------------------------------------------
/pkg/utils/ip_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils_test
6 |
7 | import (
8 | . "github.com/onsi/ginkgo/v2"
9 | . "github.com/onsi/gomega"
10 |
11 | "github.com/rocboss/paopao-ce/pkg/utils"
12 | )
13 |
14 | var _ = Describe("Ip", Ordered, func() {
15 | type iplocs []struct {
16 | ip string
17 | location string
18 | }
19 | var samples iplocs
20 |
21 | BeforeAll(func() {
22 | samples = iplocs{
23 | {
24 | ip: "",
25 | location: "",
26 | },
27 | {
28 | ip: "103.197.70.244",
29 | location: "香港",
30 | },
31 | }
32 | })
33 |
34 | It("get ip location", func() {
35 | for _, t := range samples {
36 | Expect(utils.GetIPLoc(t.ip)).To(Equal(t.location))
37 | }
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/pkg/utils/iploc/iploc_suite_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package iploc_test
6 |
7 | import (
8 | "testing"
9 |
10 | . "github.com/onsi/ginkgo/v2"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func TestIploc(t *testing.T) {
15 | RegisterFailHandler(Fail)
16 | RunSpecs(t, "Iploc Suite")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/utils/iploc/iploc_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package iploc_test
6 |
7 | import (
8 | . "github.com/onsi/ginkgo/v2"
9 | . "github.com/onsi/gomega"
10 |
11 | "github.com/rocboss/paopao-ce/pkg/utils/iploc"
12 | )
13 |
14 | var _ = Describe("Iploc", Ordered, func() {
15 | type iplocCases []struct {
16 | ip string
17 | country string
18 | city string
19 | }
20 | var samples iplocCases
21 |
22 | BeforeAll(func() {
23 | samples = iplocCases{
24 | {
25 | ip: "127.0.0.1",
26 | country: "本机地址",
27 | city: " CZ88.NET",
28 | },
29 | {
30 | ip: "180.89.94.9",
31 | country: "北京市",
32 | city: "鹏博士宽带",
33 | },
34 | }
35 | })
36 |
37 | It("find country and city by ip", func() {
38 | for _, t := range samples {
39 | country, city := iploc.Find(t.ip)
40 | Expect(country).To(Equal(t.country))
41 | Expect(city).To(Equal(t.city))
42 | }
43 | })
44 | })
45 |
--------------------------------------------------------------------------------
/pkg/utils/iploc/qqwry.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/pkg/utils/iploc/qqwry.dat
--------------------------------------------------------------------------------
/pkg/utils/md5.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils
6 |
7 | import (
8 | "crypto/md5"
9 | "encoding/hex"
10 | )
11 |
12 | func EncodeMD5(value string) string {
13 | m := md5.New()
14 | m.Write([]byte(value))
15 |
16 | return hex.EncodeToString(m.Sum(nil))
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/utils/md5_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils_test
6 |
7 | import (
8 | . "github.com/onsi/ginkgo/v2"
9 | . "github.com/onsi/gomega"
10 |
11 | "github.com/rocboss/paopao-ce/pkg/utils"
12 | )
13 |
14 | var _ = Describe("Md5", Ordered, func() {
15 | type md5Cases []struct {
16 | value string
17 | md5 string
18 | }
19 | var samples md5Cases
20 |
21 | BeforeAll(func() {
22 | samples = md5Cases{
23 | {
24 | value: "123456",
25 | md5: "e10adc3949ba59abbe56e057f20f883e",
26 | },
27 | {
28 | value: "",
29 | md5: "d41d8cd98f00b204e9800998ecf8427e", // really odd, why?
30 | },
31 | {
32 | value: "paopaocestr",
33 | md5: "8a5033dda1a8919224c66e68d846a289",
34 | },
35 | }
36 | })
37 |
38 | It("encode md5", func() {
39 | for _, t := range samples {
40 | Expect(utils.EncodeMD5(t.value)).To(Equal(t.md5))
41 | }
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/pkg/utils/utils_suite_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package utils_test
6 |
7 | import (
8 | "testing"
9 |
10 | . "github.com/onsi/ginkgo/v2"
11 | . "github.com/onsi/gomega"
12 | )
13 |
14 | func TestUtil(t *testing.T) {
15 | RegisterFailHandler(Fail)
16 | RunSpecs(t, "Utils Suite")
17 | }
18 |
--------------------------------------------------------------------------------
/pkg/version/build.go:
--------------------------------------------------------------------------------
1 | // Copyright 2025 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package version
6 |
7 | import (
8 | "debug/buildinfo"
9 | "os"
10 | "time"
11 | )
12 |
13 | var (
14 | BuildTime = time.Now()
15 | )
16 |
17 | func init() {
18 | exe, err := os.Executable()
19 | if err != nil {
20 | return
21 | }
22 | info, err := buildinfo.ReadFile(exe)
23 | if err != nil {
24 | return
25 | }
26 | for _, s := range info.Settings {
27 | if s.Key == "vcs.time" && s.Value != "" {
28 | if t, err := time.Parse(time.RFC3339, s.Value); err == nil {
29 | BuildTime = t
30 | }
31 | break
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/version/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | package version
6 |
7 | import (
8 | "fmt"
9 | )
10 |
11 | const (
12 | series = "v0.6-alpha"
13 | )
14 |
15 | var (
16 | version = "unknown"
17 | commitID = "unknown"
18 | buildDate = "unknown"
19 | buildTags = "unknown"
20 | )
21 |
22 | type BuildInfo struct {
23 | Series string `json:"series"`
24 | Version string `json:"version"`
25 | Sum string `json:"sum"`
26 | BuildDate string `json:"build_date"`
27 | BuildTags string `json:"build_tags"`
28 | }
29 |
30 | func VersionInfo() string {
31 | return fmt.Sprintf("paopao %s (build:%s %s)", version, commitID, buildDate)
32 | }
33 |
34 | func ReadBuildInfo() *BuildInfo {
35 | return &BuildInfo{
36 | Series: series,
37 | Version: version,
38 | Sum: commitID,
39 | BuildDate: buildDate,
40 | BuildTags: buildTags,
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/proto/RADME.md:
--------------------------------------------------------------------------------
1 | ### gRPC API
2 | This directory contain some gRPC API define files.
3 |
--------------------------------------------------------------------------------
/proto/buf.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | breaking:
3 | use:
4 | - FILE
5 | lint:
6 | use:
7 | - DEFAULT
8 | deps:
9 | - buf.build/googleapis/googleapis
10 |
--------------------------------------------------------------------------------
/proto/core/v1/auth.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package core.v1;
4 |
5 | option go_package = "github.com/rocboss/paopao-ce/auto/connect/core/v1;corev1";
6 |
7 | message User {
8 | string phone_num = 1;
9 | }
10 |
11 | message UserVerify {
12 | string phone_num = 1;
13 | string verification_code = 2;
14 | }
15 |
16 | message LoginReply {
17 | int32 status_code = 1;
18 | string token = 2;
19 | }
20 |
21 | message ActionReply {
22 | int32 status_code = 1;
23 | }
24 |
25 | service AuthenticateService {
26 | rpc preLogin(User) returns (ActionReply);
27 | rpc login(User) returns (LoginReply);
28 | rpc logout(User) returns (ActionReply);
29 | }
30 |
--------------------------------------------------------------------------------
/proto/greet/v1/greet.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package greet.v1;
4 |
5 | option go_package = "github.com/rocboss/paopao-ce/auto/rpc/greet/v1;greetv1";
6 |
7 | message GreetRequest {
8 | string name = 1;
9 | }
10 |
11 | message GreetResponse {
12 | string greeting = 1;
13 | }
14 |
15 | service GreetService {
16 | rpc Greet(GreetRequest) returns (GreetResponse) {}
17 | }
--------------------------------------------------------------------------------
/scripts/README:
--------------------------------------------------------------------------------
1 | All files in subdirectories are templates, do modifications based on your environment first.
--------------------------------------------------------------------------------
/scripts/docker/Dockerfile.backend-builder:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM golang:1.24-alpine3.21
3 | RUN apk --no-cache --no-progress add --virtual \
4 | build-deps \
5 | build-base \
6 | git
7 |
--------------------------------------------------------------------------------
/scripts/docker/Dockerfile.backend-runner:
--------------------------------------------------------------------------------
1 | # syntax=docker/dockerfile:1
2 | FROM alpine:3.21
3 | ENV TZ=Asia/Shanghai
4 | RUN apk update && apk add --no-cache ca-certificates && update-ca-certificates
5 |
--------------------------------------------------------------------------------
/scripts/docker/README.md:
--------------------------------------------------------------------------------
1 | ### Dockerfile builer pre-build images
2 |
3 | ```sh
4 | docker buildx build -t bitbus/paopao-ce-backend-builder:latest -f Dockerfile.backend-builder .
5 | docker buildx build -t bitbus/paopao-ce-backend-runner:latest -f Dockerfile.backend-runner .
6 | cd ../../ && docker buildx build -t bitbus/paopao-ce-allinone-runner:latest -f scripts/docker/Dockerfile.allinone-runner .
7 | ```
--------------------------------------------------------------------------------
/scripts/launchd/info.paopao.web.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | info.paopao.web
7 |
8 |
9 | UserName
10 | paopao
11 | GroupName
12 | paopao
13 | ProgramArguments
14 |
15 |
16 |
17 | /Users/paopao/app/paopao
18 | serve
19 |
20 | RunAtLoad
21 |
22 | KeepAlive
23 |
24 |
25 |
26 | WorkingDirectory
27 | /Users/paopao/app/
28 | StandardOutPath
29 | /Users/paopao/app/log/stdout.log
30 | StandardErrorPath
31 | /Users/paopao/app/log/stderr.log
32 |
33 |
34 |
--------------------------------------------------------------------------------
/scripts/migration/embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build migration
6 | // +build migration
7 |
8 | package migration
9 |
10 | import (
11 | "embed"
12 | )
13 |
14 | //go:embed **/*
15 | var Files embed.FS
16 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0001_initialize_schema.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_attachment`;
2 | DROP TABLE IF EXISTS `p_captcha`;
3 | DROP TABLE IF EXISTS `p_comment`;
4 | DROP TABLE IF EXISTS `p_comment_content`;
5 | DROP TABLE IF EXISTS `p_comment_reply`;
6 | DROP TABLE IF EXISTS `p_message`;
7 | DROP TABLE IF EXISTS `p_post`;
8 | DROP TABLE IF EXISTS `p_post_attachment_bill`;
9 | DROP TABLE IF EXISTS `p_post_collection`;
10 | DROP TABLE IF EXISTS `p_post_content`;
11 | DROP TABLE IF EXISTS `p_post_star`;
12 | DROP TABLE IF EXISTS `p_tag`;
13 | DROP TABLE IF EXISTS `p_user`;
14 | DROP TABLE IF EXISTS `p_wallet_recharge`;
15 | DROP TABLE IF EXISTS `p_wallet_statement`;
16 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0002_post_visibility.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` DROP COLUMN `visibility`;
2 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0002_post_visibility.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` ADD COLUMN `visibility` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '可见性 0公开 1私密 2好友可见';
2 | CREATE INDEX `idx_visibility` ON `p_post` ( `visibility` ) USING BTREE;
3 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0003_feature_contact.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_contact`;
2 | DROP TABLE IF EXISTS `p_contact_group`;
3 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0005_share_count.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` DROP COLUMN `share_count`;
2 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0005_share_count.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` ADD COLUMN `share_count` BIGINT unsigned NOT NULL DEFAULT 0;
--------------------------------------------------------------------------------
/scripts/migration/mysql/0006_topic_follow.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_topic_user`;
--------------------------------------------------------------------------------
/scripts/migration/mysql/0006_topic_follow.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `p_topic_user` (
2 | `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
3 | `topic_id` BIGINT UNSIGNED NOT NULL COMMENT '标签ID',
4 | `user_id` BIGINT UNSIGNED NOT NULL COMMENT '创建者ID',
5 | `alias_name` VARCHAR ( 255 ) COMMENT '别名',
6 | `remark` VARCHAR ( 512 ) COMMENT '备注',
7 | `quote_num` BIGINT UNSIGNED COMMENT '引用数',
8 | `is_top` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否置顶 0 为未置顶、1 为已置顶',
9 | `created_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '创建时间',
10 | `modified_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '修改时间',
11 | `deleted_on` BIGINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '删除时间',
12 | `is_del` TINYINT UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
13 | `reserve_a` VARCHAR ( 255 ) COMMENT '保留字段a',
14 | `reserve_b` VARCHAR ( 255 ) COMMENT '保留字段b',
15 | PRIMARY KEY ( `id` ) USING BTREE,
16 | UNIQUE KEY `idx_topic_user_uid_tid` ( `topic_id`, `user_id` ) USING BTREE
17 | ) ENGINE = INNODB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户话题';
--------------------------------------------------------------------------------
/scripts/migration/mysql/0007_comment_thumbs.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_comment` DROP COLUMN `thumbs_up_count`;
2 | ALTER TABLE `p_comment` DROP COLUMN `thumbs_down_count`;
3 | ALTER TABLE `p_comment_reply` DROP COLUMN `thumbs_up_count`;
4 | ALTER TABLE `p_comment_reply` DROP COLUMN `thumbs_down_count`;
5 |
6 | DROP TABLE IF EXISTS `p_tweet_comment_thumbs`;
7 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0008_content_type_alter.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post_content` MODIFY COLUMN `content` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
2 | ALTER TABLE `p_comment_content` MODIFY COLUMN `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
3 | ALTER TABLE `p_comment_reply` MODIFY COLUMN `content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
--------------------------------------------------------------------------------
/scripts/migration/mysql/0008_content_type_alter.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post_content` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
2 | ALTER TABLE `p_comment_content` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
3 | ALTER TABLE `p_comment_reply` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
4 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0009_create_view_post_filter.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_post_by_media;
2 | DROP VIEW IF EXISTS p_post_by_comment;
3 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0009_create_view_post_filter.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_post_by_media AS
2 | SELECT post.*
3 | FROM
4 | ( SELECT DISTINCT post_id FROM p_post_content WHERE ( TYPE = 3 OR TYPE = 4 OR TYPE = 7 OR TYPE = 8 ) AND is_del = 0 ) media
5 | JOIN p_post post ON media.post_id = post.ID
6 | WHERE
7 | post.is_del = 0;
8 |
9 | CREATE VIEW p_post_by_comment AS
10 | SELECT P.*, C.user_id comment_user_id
11 | FROM
12 | (
13 | SELECT
14 | post_id,
15 | user_id
16 | FROM
17 | p_comment
18 | WHERE
19 | is_del = 0 UNION
20 | SELECT
21 | post_id,
22 | reply.user_id user_id
23 | FROM
24 | p_comment_reply reply
25 | JOIN p_comment COMMENT ON reply.comment_id = COMMENT.ID
26 | WHERE
27 | reply.is_del = 0
28 | AND COMMENT.is_del = 0
29 | )
30 | C JOIN p_post P ON C.post_id = P.ID
31 | WHERE
32 | P.is_del = 0;
33 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0010_user_following.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_following`;
2 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0010_user_following.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE `p_following` (
2 | `id` bigint unsigned NOT NULL AUTO_INCREMENT,
3 | `user_id` bigint unsigned NOT NULL,
4 | `follow_id` bigint unsigned NOT NULL,
5 | `is_del` tinyint NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
6 | `created_on` bigint unsigned NOT NULL DEFAULT '0',
7 | `modified_on` bigint unsigned NOT NULL DEFAULT '0',
8 | `deleted_on` bigint unsigned NOT NULL DEFAULT '0',
9 | PRIMARY KEY (`id`) USING BTREE,
10 | KEY `idx_following_user_follow` (`user_id`,`follow_id`) USING BTREE
11 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
12 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0011_home_timeline.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_post_metric`;
2 |
3 | -- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
4 | -- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
5 | UPDATE p_post a, p_post b
6 | SET a.visibility = (
7 | CASE b.visibility
8 | WHEN 90 THEN 0
9 | WHEN 0 THEN 1
10 | WHEN 50 THEN 2
11 | WHEN 60 THEN 3
12 | ELSE 0
13 | END
14 | )
15 | WHERE a.ID = b.ID;
16 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0012_comment_essence.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_comment` DROP COLUMN `is_essence`;
--------------------------------------------------------------------------------
/scripts/migration/mysql/0012_comment_essence.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_comment` ADD COLUMN `is_essence` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否精选';
2 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0013_rank_metrics.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_comment` DROP COLUMN `reply_count`;
2 | DROP TABLE IF EXISTS `p_comment_metric`;
3 | DROP TABLE IF EXISTS `p_user_metric`;
4 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0014_user_relation_view.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_user_relation;
2 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0014_user_relation_view.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_user_relation AS
2 | SELECT user_id, friend_id he_uid, 5 AS style
3 | FROM p_contact WHERE status=2 AND is_del=0
4 | UNION
5 | SELECT user_id, follow_id he_uid, 10 AS style
6 | FROM p_following WHERE is_del=0;
7 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0015_topic_user_pin.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_topic_user` DROP COLUMN `is_pin`;
2 | DROP INDEX IF EXISTS `idx_topic_user_uid_ispin`;
3 |
--------------------------------------------------------------------------------
/scripts/migration/mysql/0015_topic_user_pin.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_topic_user` ADD COLUMN `is_pin` TINYINT NOT NULL DEFAULT 0 COMMENT '是否钉住 0 为未钉住、1 为已钉住';
2 | CREATE INDEX `idx_topic_user_uid_ispin` ON `p_topic_user` (`user_id`, `is_pin`) USING BTREE;
3 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0001_initialize_schema.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS p_attachment;
2 | DROP TABLE IF EXISTS p_captcha;
3 | DROP TABLE IF EXISTS p_comment;
4 | DROP TABLE IF EXISTS p_comment_content;
5 | DROP TABLE IF EXISTS p_comment_reply;
6 | DROP TABLE IF EXISTS p_message;
7 | DROP TABLE IF EXISTS p_post;
8 | DROP TABLE IF EXISTS p_post_attachment_bill;
9 | DROP TABLE IF EXISTS p_post_collection;
10 | DROP TABLE IF EXISTS p_post_content;
11 | DROP TABLE IF EXISTS p_post_star;
12 | DROP TABLE IF EXISTS p_tag;
13 | DROP TABLE IF EXISTS p_user;
14 | DROP TABLE IF EXISTS p_wallet_recharge;
15 | DROP TABLE IF EXISTS p_wallet_statement;
16 | DROP SEQUENCE IF EXISTS post_id_seq;
17 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0002_post_visibility.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_post DROP COLUMN visibility;
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0002_post_visibility.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_post ADD COLUMN visibility SMALLINT NOT NULL DEFAULT 0; -- 可见性 0公开 1私密 2好友可见
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0003_feature_contact.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS p_contact;
2 | DROP TABLE IF EXISTS p_contact_group;
3 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0004_share_count.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_post DROP COLUMN share_count;
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0004_share_count.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_post ADD COLUMN share_count BIGINT NOT NULL DEFAULT 0; -- 分享数
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0005_topic_follow.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS p_topic_user;
--------------------------------------------------------------------------------
/scripts/migration/postgres/0005_topic_follow.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE p_topic_user (
2 | ID BIGSERIAL PRIMARY KEY,
3 | topic_id BIGINT NOT NULL,-- 标签ID
4 | user_id BIGINT NOT NULL,-- 创建者ID
5 | alias_name VARCHAR ( 255 ),-- 别名
6 | remark VARCHAR ( 512 ),-- 备注
7 | quote_num BIGINT,-- 引用数
8 | is_top SMALLINT NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
9 | created_on BIGINT NOT NULL DEFAULT 0,-- 创建时间
10 | modified_on BIGINT NOT NULL DEFAULT 0,-- 修改时间
11 | deleted_on BIGINT NOT NULL DEFAULT 0,-- 删除时间
12 | is_del SMALLINT NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
13 | reserve_a VARCHAR ( 255 ),-- 保留字段a
14 | reserve_b VARCHAR ( 255 ) -- 保留字段b
15 | );
16 | CREATE UNIQUE INDEX idx_topic_user_uid_tid ON p_topic_user USING btree ( topic_id, user_id );
--------------------------------------------------------------------------------
/scripts/migration/postgres/0006_comment_thumbs.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_comment DROP COLUMN thumbs_up_count;
2 | ALTER TABLE p_comment DROP COLUMN thumbs_down_count;
3 | ALTER TABLE p_comment_reply DROP COLUMN thumbs_up_count;
4 | ALTER TABLE p_comment_reply DROP COLUMN thumbs_down_count;
5 |
6 | DROP TABLE IF EXISTS p_tweet_comment_thumbs;
7 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0006_comment_thumbs.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_comment ADD COLUMN thumbs_up_count INT NOT NULL DEFAULT 0;
2 | ALTER TABLE p_comment ADD COLUMN thumbs_down_count INT NOT NULL DEFAULT 0;
3 | ALTER TABLE p_comment_reply ADD COLUMN thumbs_up_count INT NOT NULL DEFAULT 0;
4 | ALTER TABLE p_comment_reply ADD COLUMN thumbs_down_count INT NOT NULL DEFAULT 0;
5 |
6 | CREATE TABLE p_tweet_comment_thumbs (
7 | ID BIGSERIAL PRIMARY KEY,
8 | user_id BIGINT NOT NULL,
9 | tweet_id BIGINT NOT NULL,
10 | comment_id BIGINT NOT NULL,
11 | reply_id BIGINT,
12 | comment_type SMALLINT NOT NULL DEFAULT 0,-- 评论类型 0为推文评论、1为评论回复
13 | is_thumbs_up SMALLINT NOT NULL DEFAULT 0,-- 是否点赞 0 为否 1为是
14 | is_thumbs_down SMALLINT NOT NULL DEFAULT 0,-- 是否点踩 0 为否 1为是
15 | created_on BIGINT NOT NULL DEFAULT 0,
16 | modified_on BIGINT NOT NULL DEFAULT 0,
17 | deleted_on BIGINT NOT NULL DEFAULT 0,
18 | is_del SMALLINT NOT NULL DEFAULT 0 -- 是否删除 0 为未删除、1 为已删除
19 | );
20 | CREATE INDEX idx_tweet_comment_thumbs_uid_tid ON p_tweet_comment_thumbs USING btree ( user_id, tweet_id );
--------------------------------------------------------------------------------
/scripts/migration/postgres/0007_content_type_alter.down.sql:
--------------------------------------------------------------------------------
1 | -- nothing
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0007_content_type_alter.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_post_content ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT '';
2 | ALTER TABLE p_comment_content ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT '';
3 | ALTER TABLE p_comment_reply ALTER COLUMN content SET DATA TYPE TEXT, ALTER COLUMN content SET NOT NULL, ALTER COLUMN content SET DEFAULT '';
4 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0008_create_view_post_filter.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_post_by_media;
2 | DROP VIEW IF EXISTS p_post_by_comment;
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0008_create_view_post_filter.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_post_by_media AS
2 | SELECT post.*
3 | FROM
4 | ( SELECT DISTINCT post_id FROM p_post_content WHERE ( TYPE = 3 OR TYPE = 4 OR TYPE = 7 OR TYPE = 8 ) AND is_del = 0 ) media
5 | JOIN p_post post ON media.post_id = post.ID
6 | WHERE
7 | post.is_del = 0;
8 |
9 | CREATE VIEW p_post_by_comment AS
10 | SELECT P.*, C.user_id comment_user_id
11 | FROM
12 | (
13 | SELECT
14 | post_id,
15 | user_id
16 | FROM
17 | p_comment
18 | WHERE
19 | is_del = 0 UNION
20 | SELECT
21 | post_id,
22 | reply.user_id user_id
23 | FROM
24 | p_comment_reply reply
25 | JOIN p_comment COMMENT ON reply.comment_id = COMMENT.ID
26 | WHERE
27 | reply.is_del = 0
28 | AND COMMENT.is_del = 0
29 | )
30 | C JOIN p_post P ON C.post_id = P.ID
31 | WHERE
32 | P.is_del = 0;
--------------------------------------------------------------------------------
/scripts/migration/postgres/0009_user_following.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS p_following;
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0009_user_following.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE p_following (
2 | id BIGSERIAL PRIMARY KEY,
3 | user_id BIGINT NOT NULL,
4 | follow_id BIGINT NOT NULL,
5 | is_del SMALLINT NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
6 | created_on BIGINT NOT NULL DEFAULT 0,
7 | modified_on BIGINT NOT NULL DEFAULT 0,
8 | deleted_on BIGINT NOT NULL DEFAULT 0
9 | );
10 | CREATE INDEX idx_following_user_follow ON p_following USING btree (user_id, follow_id);
11 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0010_home_timeline.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS p_post_metric;
2 |
3 | -- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
4 | -- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
5 | UPDATE p_post a
6 | SET visibility = (
7 | SELECT
8 | CASE visibility
9 | WHEN 90 THEN 0
10 | WHEN 0 THEN 1
11 | WHEN 50 THEN 2
12 | WHEN 60 THEN 3
13 | ELSE 0
14 | END
15 | FROM
16 | p_post b
17 | WHERE
18 | a.ID = b.ID
19 | );
--------------------------------------------------------------------------------
/scripts/migration/postgres/0010_home_timeline.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE p_post_metric (
2 | ID BIGSERIAL PRIMARY KEY,
3 | post_id BIGINT NOT NULL,
4 | rank_score BIGINT NOT NULL DEFAULT 0,
5 | incentive_score INT NOT NULL DEFAULT 0,
6 | decay_factor INT NOT NULL DEFAULT 0,
7 | motivation_factor INT NOT NULL DEFAULT 0,
8 | is_del SMALLINT NOT NULL DEFAULT 0,
9 | created_on BIGINT NOT NULL DEFAULT 0,
10 | modified_on BIGINT NOT NULL DEFAULT 0,
11 | deleted_on BIGINT NOT NULL DEFAULT 0
12 | );
13 | CREATE INDEX idx_post_metric_post_id_rank_score ON p_post_metric USING btree ( post_id, rank_score );
14 |
15 | INSERT INTO p_post_metric ( post_id, rank_score, created_on ) SELECT ID AS
16 | post_id,
17 | comment_count + upvote_count * 2 + collection_count * 4 AS rank_score,
18 | created_on
19 | FROM
20 | p_post
21 | WHERE
22 | is_del = 0;
23 |
24 | -- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
25 | -- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
26 | UPDATE p_post a
27 | SET visibility = (
28 | SELECT
29 | CASE visibility
30 | WHEN 0 THEN 90
31 | WHEN 1 THEN 0
32 | WHEN 2 THEN 50
33 | WHEN 3 THEN 60
34 | ELSE 0
35 | END
36 | FROM
37 | p_post b
38 | WHERE
39 | a.ID = b.ID
40 | );
41 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0011_comment_essence.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_comment DROP COLUMN is_essence;
--------------------------------------------------------------------------------
/scripts/migration/postgres/0011_comment_essence.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_comment ADD COLUMN is_essence SMALLINT NOT NULL DEFAULT 0;
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0012_rank_metrics.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_comment DROP COLUMN IF EXISTS reply_count;
2 | DROP TABLE IF EXISTS p_comment_metric;
3 | DROP TABLE IF EXISTS p_user_metric;
4 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0013_user_relation_view.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_user_relation;
2 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0013_user_relation_view.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_user_relation AS
2 | SELECT user_id, friend_id he_uid, 5 AS style
3 | FROM p_contact WHERE status=2 AND is_del=0
4 | UNION
5 | SELECT user_id, follow_id he_uid, 10 AS style
6 | FROM p_following WHERE is_del=0;
7 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0014_topic_user_pin.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_topic_user DROP COLUMN is_pin;
2 | DROP INDEX IF EXISTS idx_topic_user_uid_ispin;
3 |
--------------------------------------------------------------------------------
/scripts/migration/postgres/0014_topic_user_pin.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE p_topic_user ADD COLUMN is_pin SMALLINT NOT NULL DEFAULT 0; -- 是否钉住 0 为未钉住、1 为已钉住
2 | CREATE INDEX idx_topic_user_uid_ispin ON p_topic_user USING btree ( user_id, is_pin );
3 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0001_initialize_schema.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS "p_attachment";
2 | DROP TABLE IF EXISTS "p_captcha";
3 | DROP TABLE IF EXISTS "p_comment";
4 | DROP TABLE IF EXISTS "p_comment_content";
5 | DROP TABLE IF EXISTS "p_comment_reply";
6 | DROP TABLE IF EXISTS "p_message";
7 | DROP TABLE IF EXISTS "p_post";
8 | DROP TABLE IF EXISTS "p_post_attachment_bill";
9 | DROP TABLE IF EXISTS "p_post_collection";
10 | DROP TABLE IF EXISTS "p_post_content";
11 | DROP TABLE IF EXISTS "p_post_star";
12 | DROP TABLE IF EXISTS "p_tag";
13 | DROP TABLE IF EXISTS "p_user";
14 | DROP TABLE IF EXISTS "p_wallet_recharge";
15 | DROP TABLE IF EXISTS "p_wallet_statement";
16 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0002_post_visibility.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` DROP COLUMN `visibility`;
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0002_post_visibility.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` ADD COLUMN `visibility` integer NOT NULL DEFAULT '0';
2 |
3 | CREATE INDEX "main"."idx_visibility"
4 | ON "p_post" (
5 | "visibility" ASC
6 | );
7 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0003_feature_contact.down.sql:
--------------------------------------------------------------------------------
1 | PRAGMA foreign_keys = false;
2 |
3 | DROP TABLE IF EXISTS "p_contact";
4 | DROP TABLE IF EXISTS "p_contact_group";
5 |
6 | PRAGMA foreign_keys = true;
7 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0005_share_count.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` DROP COLUMN `share_count`;
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0005_share_count.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE `p_post` ADD COLUMN `share_count` integer NOT NULL DEFAULT 0;
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0006_topic_follow.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS `p_topic_user`;
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0006_topic_follow.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "p_topic_user" (
2 | "id" integer,
3 | "topic_id" integer NOT NULL,-- 标签ID
4 | "user_id" integer NOT NULL,-- 创建者ID
5 | "alias_name" text ( 255 ),-- 别名
6 | "remark" text ( 512 ),-- 备注
7 | "quote_num" integer,-- 引用数
8 | "is_top" integer NOT NULL DEFAULT 0,-- 是否置顶 0 为未置顶、1 为已置顶
9 | "created_on" integer NOT NULL DEFAULT 0,-- 创建时间
10 | "modified_on" integer NOT NULL DEFAULT 0,-- 修改时间
11 | "deleted_on" integer NOT NULL DEFAULT 0,-- 删除时间
12 | "is_del" integer NOT NULL DEFAULT 0,-- 是否删除 0 为未删除、1 为已删除
13 | "reserve_a" text,-- 保留字段a
14 | "reserve_b" text,-- 保留字段b
15 | PRIMARY KEY ( "id" )
16 | );
17 | CREATE UNIQUE INDEX "idx_topic_user_uid_tid" ON "p_topic_user" ( "topic_id", "user_id" );
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0007_comment_thumbs.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_comment" DROP COLUMN "thumbs_up_count";
2 | ALTER TABLE "p_comment" DROP COLUMN "thumbs_down_count";
3 | ALTER TABLE "p_comment_reply" DROP COLUMN "thumbs_up_count";
4 | ALTER TABLE "p_comment_reply" DROP COLUMN "thumbs_down_count";
5 |
6 | DROP TABLE IF EXISTS "p_tweet_comment_thumbs";
7 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0007_comment_thumbs.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_comment" ADD COLUMN "thumbs_up_count" integer NOT NULL DEFAULT 0;
2 | ALTER TABLE "p_comment" ADD COLUMN "thumbs_down_count" integer NOT NULL DEFAULT 0;
3 | ALTER TABLE "p_comment_reply" ADD COLUMN "thumbs_up_count" integer NOT NULL DEFAULT 0;
4 | ALTER TABLE "p_comment_reply" ADD COLUMN "thumbs_down_count" integer NOT NULL DEFAULT 0;
5 |
6 | CREATE TABLE "p_tweet_comment_thumbs" (
7 | "id" integer PRIMARY KEY,
8 | "user_id" integer NOT NULL,
9 | "tweet_id" integer NOT NULL,
10 | "comment_id" integer NOT NULL,
11 | "reply_id" integer,
12 | "comment_type" integer NOT NULL DEFAULT 0, -- 评论类型 0为推文评论、1为评论回复
13 | "is_thumbs_up" integer NOT NULL DEFAULT 0, -- 是否点赞 0 为否 1为是
14 | "is_thumbs_down" integer NOT NULL DEFAULT 0, -- 是否点踩 0 为否 1为是
15 | "created_on" integer NOT NULL DEFAULT 0,
16 | "modified_on" integer NOT NULL DEFAULT 0,
17 | "deleted_on" integer NOT NULL DEFAULT 0,
18 | "is_del" integer NOT NULL DEFAULT 0 -- 是否删除 0 为未删除、1 为已删除
19 | );
20 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0008_content_type_alter.down.sql:
--------------------------------------------------------------------------------
1 | -- nothing
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0009_create_view_post_filter.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_post_by_media;
2 | DROP VIEW IF EXISTS p_post_by_comment;
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0009_create_view_post_filter.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_post_by_media AS
2 | SELECT post.*
3 | FROM
4 | ( SELECT DISTINCT post_id FROM p_post_content WHERE ( TYPE = 3 OR TYPE = 4 OR TYPE = 7 OR TYPE = 8 ) AND is_del = 0 ) media
5 | JOIN p_post post ON media.post_id = post.ID
6 | WHERE
7 | post.is_del = 0;
8 |
9 | CREATE VIEW p_post_by_comment AS
10 | SELECT P.*, C.user_id comment_user_id
11 | FROM
12 | (
13 | SELECT
14 | post_id,
15 | user_id
16 | FROM
17 | p_comment
18 | WHERE
19 | is_del = 0 UNION
20 | SELECT
21 | post_id,
22 | reply.user_id user_id
23 | FROM
24 | p_comment_reply reply
25 | JOIN p_comment COMMENT ON reply.comment_id = COMMENT.ID
26 | WHERE
27 | reply.is_del = 0
28 | AND COMMENT.is_del = 0
29 | )
30 | C JOIN p_post P ON C.post_id = P.ID
31 | WHERE
32 | P.is_del = 0;
33 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0010_user_following.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS "p_following";
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0010_user_following.up.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "p_following" (
2 | "id" integer NOT NULL,
3 | "user_id" integer NOT NULL,
4 | "follow_id" integer NOT NULL,
5 | "is_del" integer NOT NULL,
6 | "created_on" integer NOT NULL,
7 | "modified_on" integer NOT NULL,
8 | "deleted_on" integer NOT NULL,
9 | PRIMARY KEY ("id")
10 | );
11 | CREATE INDEX "idx_following_user_follow"
12 | ON "p_following" (
13 | "user_id" ASC,
14 | "follow_id" ASC
15 | );
16 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0011_home_timeline.down.sql:
--------------------------------------------------------------------------------
1 | DROP TABLE IF EXISTS "p_post_metric";
2 |
3 | -- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
4 | -- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
5 | UPDATE p_post AS a
6 | SET visibility = (
7 | SELECT
8 | CASE visibility
9 | WHEN 90 THEN 0
10 | WHEN 0 THEN 1
11 | WHEN 50 THEN 2
12 | WHEN 60 THEN 3
13 | ELSE 0
14 | END
15 | FROM
16 | p_post AS b
17 | WHERE
18 | a.ID = b.ID
19 | );
20 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0012_comment_essence.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_comment" DROP COLUMN "is_essence";
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0012_comment_essence.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_comment" ADD COLUMN "is_essence" integer NOT NULL DEFAULT 0;
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0013_rank_metrics.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_comment" DROP COLUMN "reply_count";
2 | DROP TABLE IF EXISTS "p_comment_metric";
3 | DROP TABLE IF EXISTS "p_user_metric";
4 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0014_user_relation_view.down.sql:
--------------------------------------------------------------------------------
1 | DROP VIEW IF EXISTS p_user_relation;
2 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0014_user_relation_view.up.sql:
--------------------------------------------------------------------------------
1 | CREATE VIEW p_user_relation AS
2 | SELECT user_id, friend_id he_uid, 5 AS style
3 | FROM p_contact WHERE status=2 AND is_del=0
4 | UNION
5 | SELECT user_id, follow_id he_uid, 10 AS style
6 | FROM p_following WHERE is_del=0;
7 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0015_topic_user_pin.down.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_topic_user" DROP COLUMN "is_pin";
2 | DROP INDEX IF EXISTS "idx_topic_user_uid_ispin";
3 |
--------------------------------------------------------------------------------
/scripts/migration/sqlite3/0015_topic_user_pin.up.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE "p_topic_user" ADD COLUMN "is_pin" integer NOT NULL DEFAULT 0;
2 | CREATE INDEX "main"."idx_topic_user_uid_ispin"
3 | ON "p_topic_user" (
4 | "user_id" ASC,
5 | "is_pin" ASC
6 | );
7 |
--------------------------------------------------------------------------------
/scripts/systemd/paopao.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=paopao-ce
3 | After=syslog.target
4 | After=network.target
5 | After=mariadb.service mysqld.service redis.service
6 |
7 | [Service]
8 | Type=simple
9 | User=paopao
10 | Group=paopao
11 | WorkingDirectory=/home/paopao/app
12 | ExecStart=/home/paopao/app/paopao serve
13 | Restart=always
14 | Environment=USER=paopao HOME=/home/paopao
15 |
16 | [Install]
17 | WantedBy=multi-user.target
18 |
--------------------------------------------------------------------------------
/web/.dockerignore:
--------------------------------------------------------------------------------
1 | components.d.ts
2 | *Dockerfile*
3 | node_modules
4 | src-tauri/target
5 |
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
26 | bun.lock
27 | yarn.lock
28 | package-lock.json
29 | components.d.ts
30 | /stats.html
31 |
--------------------------------------------------------------------------------
/web/Dockerfile:
--------------------------------------------------------------------------------
1 | # build frontend
2 | FROM node:19-alpine as frontend
3 | ARG API_HOST
4 | ARG USE_DIST=no
5 | WORKDIR /web
6 | COPY . .
7 | RUN [ empty$API_HOST = empty ] || echo "VITE_HOST=$API_HOST" >.env.local
8 | RUN [ $USE_DIST != no ] || (yarn && yarn build)
9 |
10 | FROM library/nginx
11 | ARG API_HOST
12 | ARG USE_DIST=no
13 | USER root
14 |
15 | # copy static files
16 | COPY --from=frontend /web/dist/ /usr/share/nginx/html/
17 |
18 | # HEALTHCHECK
19 | HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD service nginx status | grep running || exit 1
20 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | paopao-ce's web frontend.
2 |
--------------------------------------------------------------------------------
/web/biome.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
3 | "organizeImports": {
4 | "enabled": true
5 | },
6 | "vcs": {
7 | "enabled": true,
8 | "clientKind": "git",
9 | "useIgnoreFile": true
10 | },
11 | "formatter": {
12 | "indentStyle": "space"
13 | },
14 | "javascript": {
15 | "formatter": {
16 | "quoteStyle": "single"
17 | }
18 | },
19 | "css": {
20 | "parser": {
21 | "cssModules": true
22 | }
23 | },
24 | "linter": {
25 | "enabled": true,
26 | "rules": {
27 | "recommended": true
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/web/dist/assets/404-BFAjwVrS.css:
--------------------------------------------------------------------------------
1 | .wrap404[data-v-e62daa85]{min-height:500px;display:flex;align-items:center;justify-content:center}.dark .main-content-wra[data-v-e62daa85]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/@emotion-WldOFDRm.js:
--------------------------------------------------------------------------------
1 | function d(e){for(var f=0,x,a=0,c=e.length;c>=4;++a,c-=4)x=e.charCodeAt(a)&255|(e.charCodeAt(++a)&255)<<8|(e.charCodeAt(++a)&255)<<16|(e.charCodeAt(++a)&255)<<24,x=(x&65535)*1540483477+((x>>>16)*59797<<16),x^=x>>>24,f=(x&65535)*1540483477+((x>>>16)*59797<<16)^(f&65535)*1540483477+((f>>>16)*59797<<16);switch(c){case 3:f^=(e.charCodeAt(a+2)&255)<<16;case 2:f^=(e.charCodeAt(a+1)&255)<<8;case 1:f^=e.charCodeAt(a)&255,f=(f&65535)*1540483477+((f>>>16)*59797<<16)}return f^=f>>>13,f=(f&65535)*1540483477+((f>>>16)*59797<<16),((f^f>>>15)>>>0).toString(36)}export{d as m};
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Anouncement-DBcpQaHQ.css:
--------------------------------------------------------------------------------
1 | .balance-wrap[data-v-d4d04859]{padding:16px}.balance-wrap .balance-line[data-v-d4d04859]{display:flex;justify-content:space-between}.balance-wrap .balance-line .balance-opts[data-v-d4d04859]{display:flex;flex-direction:column}.bill-line[data-v-d4d04859]{padding:16px;display:flex;justify-content:space-between}.bill-line .income[data-v-d4d04859],.bill-line .out[data-v-d4d04859]{font-weight:700}.bill-line .income[data-v-d4d04859]{color:#18a058}.pagination-wrap[data-v-d4d04859]{padding:10px;display:flex;justify-content:center;overflow:hidden}.qrcode-wrap[data-v-d4d04859]{display:flex;flex-direction:column;align-items:center;justify-content:center}.qrcode-wrap .pay-tips[data-v-d4d04859]{margin-top:16px}.qrcode-wrap .pay-sub-tips[data-v-d4d04859]{margin-top:4px;font-size:12px;opacity:.75;display:flex;align-items:center}.dark .income[data-v-d4d04859]{color:#63e2b7}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Collection-CjoOEYyd.css:
--------------------------------------------------------------------------------
1 | .load-more[data-v-735372fb]{margin:20px}.load-more .load-more-wrap[data-v-735372fb]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-735372fb]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-735372fb],.dark .empty-wrap[data-v-735372fb],.dark .skeleton-wrap[data-v-735372fb]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Contacts-BHvHu1HY.css:
--------------------------------------------------------------------------------
1 | .contact-item[data-v-42e975ce]{width:100%;box-sizing:border-box;padding:12px 16px}.contact-item[data-v-42e975ce]:hover{background:#f7f9f9}.contact-item .nickname-wrap[data-v-42e975ce],.contact-item .username-wrap[data-v-42e975ce]{line-height:16px;font-size:16px}.contact-item .top-tag[data-v-42e975ce]{transform:scale(.75)}.contact-item .user-info .info-item[data-v-42e975ce]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.contact-item .item-header-extra[data-v-42e975ce]{display:flex;align-items:center;opacity:.75}.dark .contact-item[data-v-42e975ce]{background-color:#101014bf}.dark .contact-item[data-v-42e975ce]:hover{background:#18181c}.load-more[data-v-69277f0c]{margin:20px}.load-more .load-more-wrap[data-v-69277f0c]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-69277f0c]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-69277f0c],.dark .empty-wrap[data-v-69277f0c],.dark .skeleton-wrap[data-v-69277f0c]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/FiraCode-Regular-CRwVj4V2.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/FiraCode-Regular-CRwVj4V2.woff2
--------------------------------------------------------------------------------
/web/dist/assets/Following-XPfMtEVy.css:
--------------------------------------------------------------------------------
1 | .follow-item[data-v-5dd79903]{display:border-box;width:100%;padding:12px 16px}.follow-item[data-v-5dd79903]:hover{background:#f7f9f9}.follow-item .nickname-wrap[data-v-5dd79903],.follow-item .username-wrap[data-v-5dd79903]{line-height:16px;font-size:16px}.follow-item .top-tag[data-v-5dd79903]{transform:scale(.75)}.follow-item .user-info .info-item[data-v-5dd79903]{font-size:14px;line-height:14px;margin-right:8px;opacity:.75}.follow-item .item-header-extra[data-v-5dd79903]{display:flex;align-items:center;opacity:.75}.dark .follow-item[data-v-5dd79903]{background-color:#101014bf}.dark .follow-item[data-v-5dd79903]:hover{background:#18181c}.main-content-wrap[data-v-dbf3bbcc]{padding:20px}.load-more[data-v-dbf3bbcc]{margin:20px}.load-more .load-more-wrap[data-v-dbf3bbcc]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-dbf3bbcc]{font-size:14px;opacity:.65}.dark .main-content-wrap[data-v-dbf3bbcc],.dark .empty-wrap[data-v-dbf3bbcc],.dark .skeleton-wrap[data-v-dbf3bbcc]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/IEnum-B3rDUvtK.js:
--------------------------------------------------------------------------------
1 | var L=(A=>(A[A.TITLE=1]="TITLE",A[A.TEXT=2]="TEXT",A[A.IMAGEURL=3]="IMAGEURL",A[A.VIDEOURL=4]="VIDEOURL",A[A.AUDIOURL=5]="AUDIOURL",A[A.LINKURL=6]="LINKURL",A[A.ATTACHMENT=7]="ATTACHMENT",A[A.CHARGEATTACHMENT=8]="CHARGEATTACHMENT",A))(L||{}),R=(A=>(A[A.PUBLIC=0]="PUBLIC",A[A.PRIVATE=1]="PRIVATE",A[A.FRIEND=2]="FRIEND",A[A.Following=3]="Following",A))(R||{}),U=(A=>(A[A.NO=0]="NO",A[A.YES=1]="YES",A))(U||{});export{L as P,R as V,U as Y};
2 |
--------------------------------------------------------------------------------
/web/dist/assets/LatoLatin-Regular-Dmlz1U0B.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/LatoLatin-Regular-Dmlz1U0B.woff2
--------------------------------------------------------------------------------
/web/dist/assets/LatoLatin-Semibold-Dbk81p2D.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/LatoLatin-Semibold-Dbk81p2D.woff2
--------------------------------------------------------------------------------
/web/dist/assets/Profile-DdmCVl1_.css:
--------------------------------------------------------------------------------
1 | .profile-baseinfo[data-v-106c824e]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-106c824e]{width:72px}.profile-baseinfo .base-info[data-v-106c824e]{position:relative;margin-left:12px;width:calc(100% - 84px)}.profile-baseinfo .base-info .username[data-v-106c824e]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .userinfo[data-v-106c824e]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-baseinfo .base-info .userinfo .info-item[data-v-106c824e]{margin-right:12px}.profile-baseinfo .base-info .top-tag[data-v-106c824e]{transform:scale(.75)}.profile-baseinfo .user-opts[data-v-106c824e]{position:relative;opacity:.75}.profile-tabs-wrap[data-v-106c824e]{padding:0 16px}.load-more[data-v-106c824e]{margin:20px}.load-more .load-more-wrap[data-v-106c824e]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-106c824e]{font-size:14px;opacity:.65}.dark .profile-wrap[data-v-106c824e],.dark .pagination-wrap[data-v-106c824e]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Setting-F1hZZqDf.css:
--------------------------------------------------------------------------------
1 | .setting-card[data-v-7bb19e7f]{margin-top:-1px;border-radius:0}.setting-card .form-submit-wrap[data-v-7bb19e7f]{display:flex;justify-content:flex-end}.setting-card .base-line[data-v-7bb19e7f]{line-height:2;display:flex;align-items:center}.setting-card .base-line .base-label[data-v-7bb19e7f]{opacity:.75;margin-right:12px}.setting-card .base-line .nickname-input[data-v-7bb19e7f]{margin-right:10px;width:120px}.setting-card .avatar[data-v-7bb19e7f]{display:flex;flex-direction:column;align-items:flex-start;margin-bottom:20px}.setting-card .avatar .avatar-img[data-v-7bb19e7f]{margin-bottom:10px}.setting-card .hash-link[data-v-7bb19e7f]{margin-left:12px}.setting-card .phone-bind-wrap[data-v-7bb19e7f]{margin-top:20px}.setting-card .phone-bind-wrap .captcha-img-wrap[data-v-7bb19e7f]{width:100%;display:flex;align-items:center}.setting-card .phone-bind-wrap .captcha-img[data-v-7bb19e7f]{width:125px;height:34px;border-radius:3px;margin-left:10px;overflow:hidden;cursor:pointer}.setting-card .phone-bind-wrap .captcha-img img[data-v-7bb19e7f]{width:100%;height:100%}.dark .setting-card[data-v-7bb19e7f]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Topic-Dk7qWh97.css:
--------------------------------------------------------------------------------
1 | .tag-item .tag-quote{margin-left:12px;font-size:14px;opacity:.75}.tag-item .tag-follow{margin-right:22px}.tag-item .options{margin-left:-32px;margin-bottom:4px;opacity:.55}.tag-item .n-thing .n-thing-header{margin-bottom:0}.tag-item .n-thing .n-thing-avatar-header-wrapper{align-items:center}.tags-wrap[data-v-f89944c3]{padding:20px}.dark .tags-wrap[data-v-f89944c3],.dark .empty-wrap[data-v-f89944c3]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/User-DlRmS904.css:
--------------------------------------------------------------------------------
1 | .profile-tabs-wrap[data-v-00ab4b4e]{padding:0 16px}.profile-baseinfo[data-v-00ab4b4e]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-00ab4b4e]{width:72px}.profile-baseinfo .base-info[data-v-00ab4b4e]{position:relative;margin-left:12px;width:calc(100% - 84px)}.profile-baseinfo .base-info .username[data-v-00ab4b4e]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .userinfo[data-v-00ab4b4e]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-baseinfo .base-info .userinfo .info-item[data-v-00ab4b4e]{margin-right:12px}.profile-baseinfo .base-info .top-tag[data-v-00ab4b4e]{transform:scale(.75)}.profile-baseinfo .user-opts[data-v-00ab4b4e]{position:absolute;top:16px;right:16px;opacity:.75}.load-more[data-v-00ab4b4e]{margin:20px}.load-more .load-more-wrap[data-v-00ab4b4e]{display:flex;flex-direction:row;justify-content:center;align-items:center;gap:14px}.load-more .load-more-wrap .load-more-spinner[data-v-00ab4b4e]{font-size:14px;opacity:.65}.dark .profile-wrap[data-v-00ab4b4e],.dark .pagination-wrap[data-v-00ab4b4e]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/Wallet-D2b31y6W.css:
--------------------------------------------------------------------------------
1 | .balance-wrap[data-v-870bd246]{padding:16px}.balance-wrap .balance-line[data-v-870bd246]{display:flex;justify-content:space-between}.balance-wrap .balance-line .balance-opts[data-v-870bd246]{display:flex;flex-direction:column}.bill-line[data-v-870bd246]{padding:16px;display:flex;justify-content:space-between}.bill-line .income[data-v-870bd246],.bill-line .out[data-v-870bd246]{font-weight:700}.bill-line .income[data-v-870bd246]{color:#18a058}.pagination-wrap[data-v-870bd246]{padding:10px;display:flex;justify-content:center;overflow:hidden}.qrcode-wrap[data-v-870bd246]{display:flex;flex-direction:column;align-items:center;justify-content:center}.qrcode-wrap .pay-tips[data-v-870bd246]{margin-top:16px}.qrcode-wrap .pay-sub-tips[data-v-870bd246]{margin-top:4px;font-size:12px;opacity:.75;display:flex;align-items:center}.dark .income[data-v-870bd246]{color:#63e2b7}.dark .main-content-wrap[data-v-870bd246]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/content-BEAgLL5B.css:
--------------------------------------------------------------------------------
1 | .link-wrap[data-v-36eef76b]{margin-bottom:10px;position:relative}.link-wrap .link-item[data-v-36eef76b]{height:22px;display:flex;align-items:center;position:relative}.link-wrap .link-item .link-txt-wrap[data-v-36eef76b]{left:calc(1em + 4px);width:calc(100% - 1em);overflow:hidden;white-space:nowrap;text-overflow:ellipsis;position:absolute}.link-wrap .link-item .link-txt-wrap .hash-link .link-txt[data-v-36eef76b]{word-break:break-all}.images-wrap{margin-top:10px}.post-img{display:flex;margin:0;border-radius:3px;overflow:hidden;background:#0000001a;border:1px solid #eee}.post-img img{width:100%;height:100%}.x1{height:174px}.x2{height:112px}.x3{height:100px}.dark .post-img{border:1px solid #333}@media screen and (max-width: 821px){.x1{height:100px}.x2{height:70px}.x3{height:50px}}.attach-item[data-v-22563084]{margin:10px 0}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/count-BK58UQ2M.js:
--------------------------------------------------------------------------------
1 | const t=e=>e>=1e3?(e/1e3).toFixed(1)+"千":e>=1e4?(e/1e4).toFixed(1)+"万":e;export{t as p};
2 |
--------------------------------------------------------------------------------
/web/dist/assets/cssfilter-l0sNRNKZ.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/dist/assets/date-fns-tz-l0sNRNKZ.js:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/web/dist/assets/dijkstrajs-C00ieaqj.js:
--------------------------------------------------------------------------------
1 | var c={exports:{}},y;function q(){return y||(y=1,function(m){var u={single_source_shortest_paths:function(o,t,e){var r={},i={};i[t]=0;var n=u.PriorityQueue.make();n.push(t,0);for(var f,p,s,h,_,v,a,d,l;!n.empty();){f=n.pop(),p=f.value,h=f.cost,_=o[p]||{};for(s in _)_.hasOwnProperty(s)&&(v=_[s],a=h+v,d=i[s],l=typeof i[s]>"u",(l||d>a)&&(i[s]=a,n.push(s,a),r[s]=p))}if(typeof e<"u"&&typeof i[e]>"u"){var k=["Could not find a path from ",t," to ",e,"."].join("");throw new Error(k)}return r},extract_shortest_path_from_predecessor_list:function(o,t){for(var e=[],r=t;r;)e.push(r),o[r],r=o[r];return e.reverse(),e},find_path:function(o,t,e){var r=u.single_source_shortest_paths(o,t,e);return u.extract_shortest_path_from_predecessor_list(r,e)},PriorityQueue:{make:function(o){var t=u.PriorityQueue,e={},r;o=o||{};for(r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e.queue=[],e.sorter=o.sorter||t.default_sorter,e},default_sorter:function(o,t){return o.cost-t.cost},push:function(o,t){var e={value:o,cost:t};this.queue.push(e),this.queue.sort(this.sorter)},pop:function(){return this.queue.shift()},empty:function(){return this.queue.length===0}}};m.exports=u}(c)),c.exports}export{q as r};
2 |
--------------------------------------------------------------------------------
/web/dist/assets/discover-tweets-DGidPW73.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/discover-tweets-DGidPW73.jpeg
--------------------------------------------------------------------------------
/web/dist/assets/errortips-bg-DB72-mLU.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/errortips-bg-DB72-mLU.png
--------------------------------------------------------------------------------
/web/dist/assets/following-tweets-BKofJ8VU.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/following-tweets-BKofJ8VU.jpeg
--------------------------------------------------------------------------------
/web/dist/assets/logo-wT_OfKx5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/dist/assets/logo-wT_OfKx5.png
--------------------------------------------------------------------------------
/web/dist/assets/main-nav-DkRZ0XqZ.css:
--------------------------------------------------------------------------------
1 | .nav-title-card{z-index:99;width:100%;top:0;position:sticky;border-radius:0;border-bottom:0;background-color:#ffffffbf;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.nav-title-card .navbar{height:30px;position:relative;display:flex;align-items:center}.nav-title-card .navbar .drawer-btn,.nav-title-card .navbar .back-btn{margin-right:8px}.nav-title-card .navbar .theme-switch-wrap{position:absolute;right:0;top:calc(50% - 9px)}.dark .nav-title-card{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/post-item-CiouHqhK.css:
--------------------------------------------------------------------------------
1 | .post-item .timestamp-mobile{margin-top:2px;opacity:.75;font-size:11px}.post-item:hover{background:#f7f9f9;cursor:pointer}.post-item{width:100%;padding:16px;box-sizing:border-box}.post-item .nickname-wrap{font-size:14px}.post-item .username-wrap{font-size:14px;opacity:.75}.post-item .top-tag{transform:scale(.75)}.post-item .item-header-extra{display:flex;align-items:center;opacity:.75}.post-item .item-header-extra .timestamp{font-size:12px}.post-item .post-text{text-align:justify;overflow:hidden;white-space:pre-wrap;word-break:break-all}.post-item .opt-item{display:flex;align-items:center;opacity:.7}.post-item .opt-item .opt-item-icon{margin-right:10px}.post-item:hover{background:#f7f9f9}.post-item.hover{cursor:pointer}.post-item .n-thing-avatar{margin-top:0}.post-item .n-thing-header{line-height:16px;margin-bottom:8px!important}.dark .post-item{background-color:#101014bf}.dark .post-item:hover{background:#18181c}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/post-skeleton-B6KFVL2X.js:
--------------------------------------------------------------------------------
1 | import{U as _}from"./naive-ui-BJojRuLw.js";import{d as c,f as s,F as p,x as l,j as n,k as o,q as t}from"./@vue-9sINKCPW.js";import{_ as m}from"./index-DxHQoSDp.js";const i={class:"user"},d={class:"content"},u=c({__name:"post-skeleton",props:{num:{default:1}},setup(f){return(a,k)=>{const e=_;return n(!0),s(p,null,l(new Array(a.num),r=>(n(),s("div",{class:"skeleton-item",key:r},[o("div",i,[t(e,{circle:"",size:"small"})]),o("div",d,[t(e,{text:"",repeat:3}),t(e,{text:"",style:{width:"60%"}})])]))),128)}}}),g=m(u,[["__scopeId","data-v-ab0015b4"]]);export{g as _};
2 |
--------------------------------------------------------------------------------
/web/dist/assets/post-skeleton-DtiTm5JG.css:
--------------------------------------------------------------------------------
1 | .skeleton-item[data-v-ab0015b4]{padding:12px;display:flex}.skeleton-item .user[data-v-ab0015b4]{width:42px}.skeleton-item .content[data-v-ab0015b4]{width:calc(100% - 42px)}.dark .skeleton-item[data-v-ab0015b4]{background-color:#101014bf}
2 |
--------------------------------------------------------------------------------
/web/dist/assets/toggle-selection-DGa8lynz.js:
--------------------------------------------------------------------------------
1 | var r,o;function c(){return o||(o=1,r=function(){var e=document.getSelection();if(!e.rangeCount)return function(){};for(var n=document.activeElement,a=[],t=0;t {
2 | event.waitUntil((async () => {})());
3 | });
4 |
5 | self.addEventListener("activate", (event) => {
6 | event.waitUntil((async () => {})());
7 | });
8 |
9 | self.addEventListener("fetch", (event) => {});
10 |
--------------------------------------------------------------------------------
/web/embed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ROC. All rights reserved.
2 | // Use of this source code is governed by a MIT style
3 | // license that can be found in the LICENSE file.
4 |
5 | //go:build !(slim && embed)
6 | // +build !slim !embed
7 |
8 | package web
9 |
10 | import (
11 | "embed"
12 | "io/fs"
13 | "net/http"
14 |
15 | embedfs "github.com/alimy/tryst/embed"
16 | "github.com/rocboss/paopao-ce/pkg/version"
17 | )
18 |
19 | //go:embed all:dist
20 | var files embed.FS
21 |
22 | // NewFileSystem get an embed static assets http.FileSystem instance.
23 | func NewFileSystem() http.FileSystem {
24 | subfs, _ := fs.Sub(files, "dist")
25 | // add custom mod time for embed fs
26 | timefs := embedfs.NewFS(subfs, version.BuildTime)
27 | return http.FS(timefs)
28 | }
29 |
--------------------------------------------------------------------------------
/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 泡泡
11 |
12 |
13 |
14 |
15 |
16 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/public/favicon.ico
--------------------------------------------------------------------------------
/web/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/public/logo.png
--------------------------------------------------------------------------------
/web/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "泡泡",
3 | "name": "泡泡",
4 | "description": "一个清新文艺的微社区",
5 | "icons": [
6 | {
7 | "src": "/logo.png",
8 | "type": "image/png",
9 | "sizes": "600x600"
10 | }
11 | ],
12 | "start_url": "/",
13 | "scope": "/",
14 | "display": "standalone"
15 | }
16 |
--------------------------------------------------------------------------------
/web/public/sw.js:
--------------------------------------------------------------------------------
1 | self.addEventListener('install', (event) => {
2 | event.waitUntil((async () => {})());
3 | });
4 |
5 | self.addEventListener('activate', (event) => {
6 | event.waitUntil((async () => {})());
7 | });
8 |
9 | self.addEventListener('fetch', (event) => {});
10 |
--------------------------------------------------------------------------------
/web/src-tauri/.gitignore:
--------------------------------------------------------------------------------
1 | # Generated by Cargo
2 | # will have compiled files and executables
3 | /target/
4 | WixTools
5 |
--------------------------------------------------------------------------------
/web/src-tauri/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "paopao"
3 | version = "0.2.0"
4 | description = "Paopao App"
5 | authors = ["Rocboss"]
6 | license = "MIT License"
7 | repository = "https://github.com/rocboss/paopao-ce"
8 | edition = "2021"
9 | rust-version = "1.60"
10 |
11 | [build-dependencies]
12 | tauri-build = { version = "1.5", features = [] }
13 |
14 | [dependencies]
15 | tauri = { version = "1.5", features = ["api-all", "macos-private-api"] }
16 |
17 | [features]
18 | # by default Tauri runs in production mode
19 | # when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
20 | default = [ "custom-protocol" ]
21 | # this feature is used used for production builds where `devPath` points to the filesystem
22 | # DO NOT remove this
23 | custom-protocol = [ "tauri/custom-protocol" ]
24 |
--------------------------------------------------------------------------------
/web/src-tauri/build.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 | tauri_build::build()
3 | }
4 |
--------------------------------------------------------------------------------
/web/src-tauri/icons/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src-tauri/icons/128x128.png
--------------------------------------------------------------------------------
/web/src-tauri/icons/128x128@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src-tauri/icons/128x128@2x.png
--------------------------------------------------------------------------------
/web/src-tauri/icons/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src-tauri/icons/32x32.png
--------------------------------------------------------------------------------
/web/src-tauri/icons/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src-tauri/icons/icon.icns
--------------------------------------------------------------------------------
/web/src-tauri/icons/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src-tauri/icons/icon.ico
--------------------------------------------------------------------------------
/web/src/api/auth.ts:
--------------------------------------------------------------------------------
1 | import { request } from '@/utils/request';
2 |
3 | /** 用户登录 */
4 | export const userLogin = (
5 | params: NetParams.AuthUserLogin,
6 | ): Promise => {
7 | return request({
8 | method: 'post',
9 | url: '/v1/auth/login',
10 | data: params,
11 | });
12 | };
13 |
14 | /** 注册用户 */
15 | export const userRegister = (
16 | params: NetParams.AuthUserRegister,
17 | ): Promise => {
18 | return request({
19 | method: 'post',
20 | url: '/v1/auth/register',
21 | data: params,
22 | });
23 | };
24 |
25 | /** 用户信息 */
26 | export const userInfo = (
27 | token: NetParams.AuthUserInfo = '',
28 | ): Promise => {
29 | return request({
30 | method: 'get',
31 | url: '/v1/user/info',
32 | headers: {
33 | Authorization: `Bearer ${token}`,
34 | },
35 | });
36 | };
37 |
38 | /** 修改用户密码,该接口暂时未使用 */
39 | export const updateUserPassword = (
40 | data: NetParams.AuthUpdateUserPassword,
41 | ): Promise => {
42 | return request({
43 | method: 'post',
44 | url: '/v1/api/user/password',
45 | data,
46 | });
47 | };
48 |
--------------------------------------------------------------------------------
/web/src/api/site.ts:
--------------------------------------------------------------------------------
1 | import { request } from '@/utils/request';
2 |
3 | /** 获取站点概要信息 */
4 | export const getSiteProfile = (): Promise => {
5 | return request({
6 | method: 'get',
7 | url: '/v1/site/profile',
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/web/src/assets/img/discover-tweets.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/assets/img/discover-tweets.jpeg
--------------------------------------------------------------------------------
/web/src/assets/img/following-tweets.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/assets/img/following-tweets.jpeg
--------------------------------------------------------------------------------
/web/src/assets/img/fresh-tweets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/assets/img/fresh-tweets.png
--------------------------------------------------------------------------------
/web/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/assets/img/logo.png
--------------------------------------------------------------------------------
/web/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/assets/logo.png
--------------------------------------------------------------------------------
/web/src/components/message-skeleton.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
20 |
21 |
--------------------------------------------------------------------------------
/web/src/components/post-skeleton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
23 |
24 |
--------------------------------------------------------------------------------
/web/src/components/post-video.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
26 |
--------------------------------------------------------------------------------
/web/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue';
2 | import router from './router';
3 | import store from './store';
4 | import App from './App.vue';
5 |
6 | import type { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider';
7 |
8 | // 通用字体
9 | import 'vfonts/Lato.css';
10 | // 等宽字体
11 | import 'vfonts/FiraCode.css';
12 |
13 | createApp(App).use(router).use(store).mount('#app');
14 |
15 | declare global {
16 | interface Window {
17 | $message: MessageApiInjection;
18 | $store: any;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/web/src/shims-vue.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.vue' {
2 | import type { DefineComponent } from 'vue';
3 | const component: DefineComponent<{}, {}, any>;
4 | export default component;
5 | }
6 |
7 | declare module '*.svg';
8 | declare module '*.png';
9 | declare module '*.jpg';
10 | declare module '*.jpeg';
11 | declare module '*.gif';
12 | declare module '*.bmp';
13 | declare module '*.tiff';
14 | declare module '*.json';
15 | declare module 'paopao-video-player';
16 |
17 | interface AnyObject {
18 | [key: string]: any;
19 | }
20 |
--------------------------------------------------------------------------------
/web/src/types/Core.d.ts:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rocboss/paopao-ce/56ab0cbf9c1cad5df207827f72d6b5ce988c03cd/web/src/types/Core.d.ts
--------------------------------------------------------------------------------
/web/src/utils/count.ts:
--------------------------------------------------------------------------------
1 | export const prettyQuoteNum = (num: number) => {
2 | if (num >= 1000) {
3 | return (num / 1000).toFixed(1) + '千';
4 | } else if (num >= 10000) {
5 | return (num / 10000).toFixed(1) + '万';
6 | }
7 | return num;
8 | };
9 |
--------------------------------------------------------------------------------
/web/src/utils/scrollToTop.ts:
--------------------------------------------------------------------------------
1 | // 滚动到顶部
2 | export const scrollToTop = (scrollDuration: number) => {
3 | var cosParameter = window.scrollY / 2;
4 | var scrollCount = 0;
5 | var oldTimestamp = performance.now();
6 | function step(newTimestamp: number) {
7 | scrollCount += Math.PI / (scrollDuration / (newTimestamp - oldTimestamp));
8 | if (scrollCount >= Math.PI) window.scrollTo(0, 0);
9 | if (window.scrollY === 0) return;
10 | window.scrollTo(
11 | 0,
12 | Math.round(cosParameter + cosParameter * Math.cos(scrollCount)),
13 | );
14 | oldTimestamp = newTimestamp;
15 | window.requestAnimationFrame(step);
16 | }
17 | window.requestAnimationFrame(step);
18 | };
19 |
--------------------------------------------------------------------------------
/web/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 | 回主页
13 |
14 |
15 |
16 |
17 |
18 |
19 |
29 |
30 |
--------------------------------------------------------------------------------
/web/src/vuex.d.ts:
--------------------------------------------------------------------------------
1 | import { ComponentCustomProperties } from 'vue';
2 | import { Store } from 'vuex';
3 |
4 | declare module '@vue/runtime-core' {
5 | // 声明自己的 store state
6 | interface State {
7 | [key: string]: any;
8 | }
9 |
10 | // 为 `this.$store` 提供类型声明
11 | interface ComponentCustomProperties {
12 | $store: Store;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "strict": true,
7 | "jsx": "preserve",
8 | "sourceMap": true,
9 | "resolveJsonModule": true,
10 | "esModuleInterop": true,
11 | "lib": ["esnext", "dom"],
12 | "types": ["node"],
13 | "paths": {
14 | "@/*": ["./src/*"]
15 | }
16 | },
17 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
18 | }
19 |
--------------------------------------------------------------------------------
/web/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig, type PluginOption } from 'vite';
2 | import path from 'path';
3 | import vue from '@vitejs/plugin-vue';
4 | import Components from 'unplugin-vue-components/vite';
5 |
6 | import { visualizer } from 'rollup-plugin-visualizer';
7 | import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | server: {
11 | host: '0.0.0.0',
12 | },
13 | plugins: [
14 | vue({
15 | reactivityTransform: [/src/],
16 | }),
17 | Components({
18 | resolvers: [NaiveUiResolver()],
19 | }),
20 | visualizer() as PluginOption,
21 | ],
22 | resolve: {
23 | alias: {
24 | '@': path.resolve(__dirname, 'src'),
25 | },
26 | },
27 | build: {
28 | chunkSizeWarningLimit: 1000,
29 | rollupOptions: {
30 | output: {
31 | manualChunks(id) {
32 | if (id.includes('node_modules')) {
33 | return id
34 | .toString()
35 | .split('node_modules/')[1]
36 | .split('/')[0]
37 | .toString();
38 | }
39 | },
40 | },
41 | },
42 | },
43 | });
44 |
--------------------------------------------------------------------------------