├── .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 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /web/src/components/post-skeleton.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | -------------------------------------------------------------------------------- /web/src/components/post-video.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | --------------------------------------------------------------------------------