diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml index 543c29d..42eb4bb 100644 --- a/.gitea/workflows/docker.yml +++ b/.gitea/workflows/docker.yml @@ -21,7 +21,7 @@ env: SSH_PASSWORD: ${{ github.ref_name == 'main' && vars.SSH_PASSWORD || vars.DEV_SSH_PASSWORD }} # TG通知 TG_BOT_TOKEN: 8114337882:AAHkEx03HSu7RxN4IHBJJEnsK9aPPzNLIk0 - TG_CHAT_ID: "-49402438031" + TG_CHAT_ID: "-4940243803" # Go构建变量 SERVICE: vpn SERVICE_STYLE: vpn diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..0f5b745 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,38 @@ +services: + mysql: + image: mysql:8.0 + container_name: ppanel-mysql + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: ppanel_dev + MYSQL_DATABASE: ppanel + MYSQL_USER: ppanel + MYSQL_PASSWORD: ppanel_dev + ports: + - "3306:3306" + volumes: + - ppanel_mysql_data:/var/lib/mysql + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-pppanel_dev"] + interval: 10s + timeout: 5s + retries: 5 + + redis: + image: redis:7-alpine + container_name: ppanel-redis + restart: unless-stopped + ports: + - "6379:6379" + volumes: + - ppanel_redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + +volumes: + ppanel_mysql_data: + ppanel_redis_data: diff --git a/grafana/provisioning/dashboards/dashboards.yml b/grafana/provisioning/dashboards/dashboards.yml new file mode 100644 index 0000000..ed36901 --- /dev/null +++ b/grafana/provisioning/dashboards/dashboards.yml @@ -0,0 +1,14 @@ +apiVersion: 1 + +providers: + - name: PPanel + orgId: 1 + folder: PPanel + folderUid: ppanel + type: file + disableDeletion: false + allowUiUpdates: true + updateIntervalSeconds: 30 + options: + path: /etc/grafana/provisioning/dashboards/json + foldersFromFilesStructure: false diff --git a/grafana/provisioning/datasources/datasources.yml b/grafana/provisioning/datasources/datasources.yml new file mode 100644 index 0000000..4320747 --- /dev/null +++ b/grafana/provisioning/datasources/datasources.yml @@ -0,0 +1,48 @@ +apiVersion: 1 + +datasources: + - name: Prometheus + uid: prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true + jsonData: + httpMethod: POST + manageAlerts: true + prometheusType: Prometheus + prometheusVersion: 2.50.0 + timeInterval: 15s + + - name: Loki + uid: loki + type: loki + access: proxy + url: http://loki:3100 + editable: true + jsonData: + derivedFields: + - datasourceUid: tempo + matcherRegex: '"(?:trace|traceID|trace_id)"\s*:\s*"([a-f0-9]{32})"' + name: TraceID + url: '$${__value.raw}' + + - name: Tempo + uid: tempo + type: tempo + access: proxy + url: http://tempo:3200 + editable: true + jsonData: + tracesToLogsV2: + datasourceUid: loki + filterByTraceID: true + filterBySpanID: false + tags: + - key: service.name + value: service_name + tracesToMetrics: + datasourceUid: prometheus + serviceMap: + datasourceUid: prometheus diff --git a/internal/logic/admin/user/updateUserBasicInfoLogic.go b/internal/logic/admin/user/updateUserBasicInfoLogic.go index 8ef0df3..60b711b 100644 --- a/internal/logic/admin/user/updateUserBasicInfoLogic.go +++ b/internal/logic/admin/user/updateUserBasicInfoLogic.go @@ -119,7 +119,12 @@ func (l *UpdateUserBasicInfoLogic) UpdateUserBasicInfo(req *types.UpdateUserBasi } userInfo.Commission = req.Commission } - tool.DeepCopy(userInfo, req) + userInfo.Avatar = req.Avatar + userInfo.ReferCode = req.ReferCode + userInfo.RefererId = req.RefererId + userInfo.Enable = &req.Enable + userInfo.IsAdmin = &req.IsAdmin + userInfo.Remark = req.Remark userInfo.OnlyFirstPurchase = &req.OnlyFirstPurchase userInfo.ReferralPercentage = req.ReferralPercentage diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..208636e --- /dev/null +++ b/log.txt @@ -0,0 +1,169 @@ +跳至主要内容 + +Grafana +探索 +Loki +搜索... +⌘+k + + +User avatar +探索 + +轮廓 +Loki logo +Loki + +转到无查询 + +拆分 + +添加 + + +过去 24 小时 + + + +运行查询 + + +分享 + + +直播 + + + + + +A +(Loki) + + + + + +Kick start your query + +Label browser +Explain query + + + +{container="ppanel-server"} |= "10013" |= "user_subscribe" + +Options +Type: Range +Line limit: 1000 +Direction: Backward +This query will process approximately 20.3 GiB. + + +添加查询 + +查询历史记录 + +查询检查器 + +日志卷 +info +Total: 717 +Loki +日志 + + +717 lines displayed +已处理的总字节数: +23.4 GB +Common labels: +container=ppanel-server +logstream=stdout +service_name=ppanel-server +已展开 + +滚动到底部 + +最新日志在前 + +搜索日志 + +去重 + +筛选级别 + +显示毫秒 +ms +换行显示 JSON ++ +Columns not supported + +突出显示文本 + +大字体 + +下载日志 + +滚动到顶部 + + +2026-04-30 08:33:29.296 info 303029-04-04 00:00:00.296 debug [GORM] SQL Executed duration=0.5ms caller=user/queryUserSubscribeLogic.go:47 sql=SELECT `user_subscribe`.`id`,`user_subscribe`.`user_id`,`user_subscribe`.`order_id`,`user_subscribe`.`subscribe_id`,`user_subscribe`.`node_group_id`,`user_subscribe`.`start_time`,`user_subscribe`.`expire_time`,`user_subscribe`.`finished_at`,`user_subscribe`.`traffic`,`user_subscribe`.`download`,`user_subscribe`.`upload`,`user_subscribe`.`token`,`user_subscribe`.`uuid`,`user_subscribe`.`status`,`user_subscribe`.`note`,`user_subscribe`.`created_at`,`user_subscribe`.`updated_at` FROM `user_subscribe` WHERE `user_id` = 19764 AND `status` IN (0,1,2,3) AND (`expire_time` > '2026-04-30 23:33:29.295' OR `finished_at` >= '2026-04-23 23:33:29.295' OR `expire_time` = '1970-01-01 08:00:00') trace=428ceb5c1001366ab0dfddaa0fd0cf8a rows=1 span=a0c44ec4143a6a77 + +2026-04-30 06:38:24.260 info 303024-04-04 00:00:00.260 debug [GORM] SQL Executed duration=0.2ms caller=group/recalculateGroupLogic.go:58 sql=UPDATE `user_subscribe` SET `node_group_id`=0,`updated_at`='2026-04-30 21:38:24.26' WHERE id = 10013 rows=1 + +2026-04-30 06:38:24.259 info 303024-04-04 00:00:00.259 debug assigning user_subscribe_id=10013 (subscribe_id=1) to node_group_id=0 (total_options=0, selected_first) caller=group/recalculateGroupLogic.go:504 + +2026-04-30 06:38:24.121 info 303024-04-04 00:00:00.121 info [SubscriptionFlow] renewal order updated existing subscription caller=common/subscriptionTrace.go:28 effective_user_id=20003 payment_id=2 expire_time=2026-05-30 21:38:24.118494775 +0800 CST user_id=20003 quantity=30 flow=order_subscription stage=subscription_renewed order_no=202604302137198938303097982 is_new_order=false user_subscribe_id=10013 subscribe_order_id=17216 subscribe_uuid_tail=936f5490 payment_method=EPay subscribe_token_tail=3cb4c6b3 user_subscribe_plan_id=1 subscribe_status=1 iap_expire_at=0 trace_type=subscription_flow parent_order_id=7448 order_type=2 order_status=4 order_subscribe_id=1 app_account_token_tail=e04447ca order_id=17216 subscription_user_id=20003 subscribe_owner_user_id=20003 + +2026-04-30 06:38:24.120 info 303024-04-04 00:00:00.120 debug [GORM] SQL Executed duration=1.7ms caller=user/subscribe.go:17 sql=UPDATE `user_subscribe` SET `id`=10013,`user_id`=20003,`order_id`=17216,`subscribe_id`=1,`node_group_id`=0,`group_locked`=false,`start_time`='2026-04-23 19:06:40.003',`expire_time`='2026-05-30 21:38:24.118',`finished_at`=NULL,`traffic`=0,`download`=0,`upload`=0,`expired_download`=0,`expired_upload`=0,`token`='e622c7a270caf7fd816f9dd83cb4c6b3',`uuid`='aabd8eb1-2569-4a69-b59c-15cd936f5490',`status`=1,`note`='',`updated_at`='2026-04-30 21:38:24.119' WHERE id = 10013 rows=1 + +2026-04-30 06:38:24.118 info 303024-04-04 00:00:00.118 debug [GORM] SQL Executed duration=0.2ms caller=user/subscribe.go:161 sql=SELECT * FROM `user_subscribe` WHERE id = 10013 ORDER BY `user_subscribe`.`id` LIMIT 1 rows=1 + +2026-04-30 06:37:25.952 info 303025-04-04 00:00:00.952 debug [GORM] SQL Executed duration=0.1ms caller=group/recalculateGroupLogic.go:58 sql=UPDATE `user_subscribe` SET `node_group_id`=0,`updated_at`='2026-04-30 21:37:25.952' WHERE id = 10013 rows=1 + +2026-04-30 06:37:19.898 info 303019-04-04 00:00:00.898 info HTTP Request duration=6.088471ms caller=middleware/loggerMiddleware.go:113 decrypted_request_body={"coupon":"","payment":2,"quantity":30,"user_subscribe_id":10013} response_body={"code":200,"data":{"data":"SxIHPdGQQbeAwx5UYMaRaouIke61GH+VRSl/iSr63r/GVLuXfd+ljlQLmukw9UE/Fdjc5oSWDq5ApVyEBR4bF79eBcuDz+cYjjmnJIn1sXt8hIPlwKlhc50TFwe8/x2RlpsHsLUYaOYOivMv/iuH/A==","time":"18ab25fb776f18b9"},"msg":"success"} ip=112.96.81.74 trace=60ff721319b3cf1b8f328dc05f01cb99 status=200 request=POST api.hifast.biz/v1/public/order/renewal query= user-agent=HiFast/1.0.6-26042218 (Android; HUAWEI PLA-AL10; 12) Flutter device_decrypt_status=success api_header= request_body={"data":"xdcFznl62TSP5AiItT+OKZWELzq3mR+cUZ2COAZ7ERslZT+M8mmEI65BfwevDWXA6l9TjMLr5sRkR4+omRV3fbH+PkLJ7kvIf9WPQ9ASaNg=","time":"2026-04-30T21:37:19.943917"} span=99fda96ceb7ba495 + +2026-04-30 06:37:19.898 info 303019-04-04 00:00:00.898 info [SubscriptionFlow] renewal order persisted caller=common/subscriptionTrace.go:28 trace_type=subscription_flow order_no=202604302137198938303097982 user_id=20003 payment_id=2 parent_order_id=7448 quantity=30 app_account_token_tail=e04447ca resolved_user_subscribe_id=10013 stage=order_created order_id=17216 order_type=2 order_status=1 subscription_user_id=20003 trace=60ff721319b3cf1b8f328dc05f01cb99 span=99fda96ceb7ba495 effective_user_id=20003 order_subscribe_id=1 requested_user_subscribe_id=10013 flow=order_subscription payment_method=EPay is_new_order=false subscribe_token_tail=3cb4c6b3 + +2026-04-30 06:37:19.894 info 303019-04-04 00:00:00.894 debug [GORM] SQL Executed duration=0.5ms caller=order/renewalLogic.go:81 rows=1 span=99fda96ceb7ba495 sql=SELECT `user_subscribe`.`id`,`user_subscribe`.`user_id`,`user_subscribe`.`order_id`,`user_subscribe`.`subscribe_id`,`user_subscribe`.`node_group_id`,`user_subscribe`.`start_time`,`user_subscribe`.`expire_time`,`user_subscribe`.`finished_at`,`user_subscribe`.`traffic`,`user_subscribe`.`download`,`user_subscribe`.`upload`,`user_subscribe`.`token`,`user_subscribe`.`uuid`,`user_subscribe`.`status`,`user_subscribe`.`note`,`user_subscribe`.`created_at`,`user_subscribe`.`updated_at` FROM `user_subscribe` WHERE id = 10013 ORDER BY `user_subscribe`.`id` LIMIT 1 trace=60ff721319b3cf1b8f328dc05f01cb99 + +2026-04-30 06:37:19.893 info 303019-04-04 00:00:00.893 info [SubscriptionFlow] renewal order creation started caller=common/subscriptionTrace.go:28 effective_user_id=20003 quantity=30 trace=60ff721319b3cf1b8f328dc05f01cb99 trace_type=subscription_flow flow=order_subscription stage=order_create_start order_kind=renewal user_id=20003 span=99fda96ceb7ba495 requested_user_subscribe_id=10013 payment_id=2 coupon= + +2026-04-30 06:36:05.808 info 30305-04-04 00:00:00.808 info HTTP Request duration=7.877334ms caller=middleware/loggerMiddleware.go:113 query= device_decrypt_status=success response_body={"code":200,"data":{"data":"ELE22mv7foWM+iFzrylO+xbiTWWO7gH9qclIxJzT8TRqS1fJN32GiWq/gtNlGpVWwX4iZoNjI0uOA/5lV4YltAfw3bL7sz+CcARgVEZ4NGFRqOt7bVm1RssagYLopQdfoueQ0Z+j7zOFkBlzNW+r3Q==","time":"18ab25ea37520766"},"msg":"success"} user-agent=HiFast/1.0.6-26042218 (Android; HUAWEI PLA-AL10; 12) Flutter request_body={"data":"a4U8LH1zN4LFF+aBBLoUHzEZoxJ4C6EL1nlDNeKb4m2Xwi8H1hk05qxbkvj3xz6kKdecrhF2G0G6rAavhNlKDDZhaHH/J5elqjw6tsQGluk=","time":"2026-04-30T21:36:05.852132"} request=POST api.hifast.biz/v1/public/order/renewal ip=112.96.81.74 decrypted_request_body={"coupon":"","payment":2,"quantity":30,"user_subscribe_id":10013} trace=7e1b88eed63e7ffba4a6e6e2482d399d status=200 api_header= span=d6bd668124fcad80 + +2026-04-30 06:36:05.808 info 30305-04-04 00:00:00.808 info [SubscriptionFlow] renewal order persisted caller=common/subscriptionTrace.go:28 app_account_token_tail=237eb1b5 span=d6bd668124fcad80 flow=order_subscription order_id=17213 order_subscribe_id=1 parent_order_id=7448 requested_user_subscribe_id=10013 trace=7e1b88eed63e7ffba4a6e6e2482d399d user_id=20003 payment_id=2 payment_method=EPay resolved_user_subscribe_id=10013 stage=order_created order_no=202604302136058021461564356 order_status=1 quantity=30 is_new_order=false trace_type=subscription_flow order_type=2 subscription_user_id=20003 effective_user_id=20003 subscribe_token_tail=3cb4c6b3 + +2026-04-30 06:36:05.802 info 30305-04-04 00:00:00.802 debug [GORM] SQL Executed duration=0.7ms caller=order/renewalLogic.go:81 sql=SELECT `user_subscribe`.`id`,`user_subscribe`.`user_id`,`user_subscribe`.`order_id`,`user_subscribe`.`subscribe_id`,`user_subscribe`.`node_group_id`,`user_subscribe`.`start_time`,`user_subscribe`.`expire_time`,`user_subscribe`.`finished_at`,`user_subscribe`.`traffic`,`user_subscribe`.`download`,`user_subscribe`.`upload`,`user_subscribe`.`token`,`user_subscribe`.`uuid`,`user_subscribe`.`status`,`user_subscribe`.`note`,`user_subscribe`.`created_at`,`user_subscribe`.`updated_at` FROM `user_subscribe` WHERE id = 10013 ORDER BY `user_subscribe`.`id` LIMIT 1 rows=1 trace=7e1b88eed63e7ffba4a6e6e2482d399d span=d6bd668124fcad80 + +2026-04-30 06:36:05.802 info 30305-04-04 00:00:00.802 info [SubscriptionFlow] renewal order creation started caller=common/subscriptionTrace.go:28 flow=order_subscription requested_user_subscribe_id=10013 payment_id=2 coupon= span=d6bd668124fcad80 trace_type=subscription_flow order_kind=renewal trace=7e1b88eed63e7ffba4a6e6e2482d399d stage=order_create_start user_id=20003 effective_user_id=20003 quantity=30 + +2026-04-30 06:35:46.036 info 303046-04-04 00:00:00.036 debug [GORM] SQL Executed duration=0.1ms caller=group/recalculateGroupLogic.go:58 sql=UPDATE `user_subscribe` SET `node_group_id`=0,`updated_at`='2026-04-30 21:35:46.036' WHERE id = 10013 rows=1 + +2026-04-30 06:35:38.276 info 303038-04-04 00:00:00.276 info HTTP Request duration=7.276753ms caller=middleware/loggerMiddleware.go:113 request=POST api.hifast.biz/v1/public/order/renewal ip=112.96.81.74 user-agent=HiFast/1.0.6-26042218 (Android; HUAWEI PLA-AL10; 12) Flutter device_decrypt_status=success trace=fd8fbb2d663ad7df82f0c25bf7fb6913 span=c6fe83c0a377d5b5 status=200 query= request_body={"data":"6cuaVRmMX/CGHQ+y0jrwjKTUx404hlB7/z+qPYpK9toUHAVMBrhVBC/cnZmzffjaPuqlUrLx9lSOTHDxv3GGqXZxDah5N4AimVY0dFsx6Bc=","time":"2026-04-30T21:35:38.294140"} decrypted_request_body={"coupon":"","payment":2,"quantity":30,"user_subscribe_id":10013} response_body={"code":200,"data":{"data":"iQSMczPB07C+ruU52emoga1TtBShOiBZOtU8jW9XzmQiKe9DjWCxGY+fFTnJ6LrQCXAQqZWwBpQgABpFWpcJ+MbRr5jk4S3ahwXzeHXBiMcGlgAJRb0G3O+K7h7z+6t+uhGNjzCE2CYFMuii+0T3rg==","time":"18ab25e3ce44be91"},"msg":"success"} api_header= + +2026-04-30 06:35:38.276 info 303038-04-04 00:00:00.276 info [SubscriptionFlow] renewal order persisted caller=common/subscriptionTrace.go:28 subscription_user_id=20003 order_subscribe_id=1 effective_user_id=20003 payment_id=2 parent_order_id=7448 subscribe_token_tail=3cb4c6b3 trace=fd8fbb2d663ad7df82f0c25bf7fb6913 span=c6fe83c0a377d5b5 flow=order_subscription payment_method=EPay quantity=30 is_new_order=false app_account_token_tail=6b5d9042 order_no=202604302135382700174133469 requested_user_subscribe_id=10013 resolved_user_subscribe_id=10013 trace_type=subscription_flow stage=order_created order_id=17211 order_type=2 order_status=1 user_id=20003 + +2026-04-30 06:35:38.270 info 303038-04-04 00:00:00.270 debug [GORM] SQL Executed duration=0.5ms caller=order/renewalLogic.go:81 rows=1 trace=fd8fbb2d663ad7df82f0c25bf7fb6913 span=c6fe83c0a377d5b5 sql=SELECT `user_subscribe`.`id`,`user_subscribe`.`user_id`,`user_subscribe`.`order_id`,`user_subscribe`.`subscribe_id`,`user_subscribe`.`node_group_id`,`user_subscribe`.`start_time`,`user_subscribe`.`expire_time`,`user_subscribe`.`finished_at`,`user_subscribe`.`traffic`,`user_subscribe`.`download`,`user_subscribe`.`upload`,`user_subscribe`.`token`,`user_subscribe`.`uuid`,`user_subscribe`.`status`,`user_subscribe`.`note`,`user_subscribe`.`created_at`,`user_subscribe`.`updated_at` FROM `user_subscribe` WHERE id = 10013 ORDER BY `user_subscribe`.`id` LIMIT 1 + +2026-04-30 06:35:38.270 info 303038-04-04 00:00:00.270 info [SubscriptionFlow] renewal order creation started caller=common/subscriptionTrace.go:28 flow=order_subscription effective_user_id=20003 coupon= trace_type=subscription_flow payment_id=2 user_id=20003 quantity=30 trace=fd8fbb2d663ad7df82f0c25bf7fb6913 span=c6fe83c0a377d5b5 stage=order_create_start order_kind=renewal requested_user_subscribe_id=10013 + +按名称搜索字段 + +Show log level +0 +字段 + +__stream_shard__ +25% + +container +100% + +level +100% + +logstream +100% + +service_name +100% diff --git a/loki/loki-config.yaml b/loki/loki-config.yaml new file mode 100644 index 0000000..8c71fd0 --- /dev/null +++ b/loki/loki-config.yaml @@ -0,0 +1,36 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + grpc_listen_port: 9096 + +common: + path_prefix: /loki + replication_factor: 1 + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + +schema_config: + configs: + - from: 2024-01-01 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +storage_config: + filesystem: + directory: /loki/chunks + +limits_config: + allow_structured_metadata: true + retention_period: 168h + +compactor: + working_directory: /loki/compactor + retention_enabled: true + delete_request_store: filesystem diff --git a/loki/promtail-config.yaml b/loki/promtail-config.yaml new file mode 100644 index 0000000..a1a2bc6 --- /dev/null +++ b/loki/promtail-config.yaml @@ -0,0 +1,47 @@ +server: + http_listen_port: 9080 + grpc_listen_port: 0 + +positions: + filename: /tmp/positions.yaml + +clients: + - url: http://loki:3100/loki/api/v1/push + +scrape_configs: + - job_name: ppanel-file + static_configs: + - targets: + - localhost + labels: + job: ppanel-server + service_name: ppanel + __path__: /var/log/ppanel-server/*.log + + - job_name: nginx-file + static_configs: + - targets: + - localhost + labels: + job: nginx + service_name: nginx + __path__: /var/log/nginx/*.log + + - job_name: docker-containers + docker_sd_configs: + - host: unix:///var/run/docker.sock + refresh_interval: 15s + relabel_configs: + - source_labels: [__meta_docker_container_name] + regex: '/(.*)' + target_label: container + - source_labels: [__meta_docker_container_label_com_docker_compose_service] + target_label: compose_service + - source_labels: [__meta_docker_container_log_stream] + target_label: stream + - source_labels: [__meta_docker_container_name] + regex: '/(.*)' + target_label: service_name + - source_labels: [__meta_docker_container_id] + target_label: __path__ + replacement: /var/lib/docker/containers/$1/$1-json.log diff --git a/prometheus/prometheus.yml b/prometheus/prometheus.yml new file mode 100644 index 0000000..13ec981 --- /dev/null +++ b/prometheus/prometheus.yml @@ -0,0 +1,50 @@ +global: + scrape_interval: 15s + evaluation_interval: 15s + +scrape_configs: + - job_name: prometheus + static_configs: + - targets: + - prometheus:9090 + + - job_name: grafana + metrics_path: /metrics + static_configs: + - targets: + - grafana:3000 + + - job_name: node-exporter + static_configs: + - targets: + - node-exporter:9100 + + - job_name: cadvisor + static_configs: + - targets: + - cadvisor:8080 + + - job_name: redis-exporter + static_configs: + - targets: + - redis-exporter:9121 + + - job_name: mysql-exporter + static_configs: + - targets: + - mysql-exporter:9104 + + - job_name: nginx-exporter + static_configs: + - targets: + - nginx-exporter:9113 + + - job_name: loki + static_configs: + - targets: + - loki:3100 + + - job_name: tempo + static_configs: + - targets: + - tempo:3200 diff --git a/tempo/tempo-config.yaml b/tempo/tempo-config.yaml new file mode 100644 index 0000000..8a68571 --- /dev/null +++ b/tempo/tempo-config.yaml @@ -0,0 +1,37 @@ +server: + http_listen_port: 3200 + grpc_listen_port: 9095 + +distributor: + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + +ingester: + max_block_duration: 5m + +compactor: + compaction: + block_retention: 168h + +storage: + trace: + backend: local + wal: + path: /var/tempo/wal + local: + path: /var/tempo/blocks + +metrics_generator: + registry: + external_labels: + source: tempo + cluster: ppanel + storage: + path: /var/tempo/generator/wal + remote_write: + - url: http://prometheus:9090/api/v1/write diff --git a/tests/config.go b/tests/config.go new file mode 100644 index 0000000..22dac06 --- /dev/null +++ b/tests/config.go @@ -0,0 +1,136 @@ +// Package tests provides HTTP client helpers for manually testing ppanel-server APIs. +// +// Usage: +// +// go test -v -run TestXxx ./tests/ +// +// Configure the constants below to match your local environment. +package tests + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "testing" +) + +// ─── Environment Configuration ──────────────────────────────────────────────── + +const ( + // BaseURL is the ppanel-server base URL (no trailing slash). + BaseURL = "http://localhost:8080" + + // AdminToken is the JWT token for an admin user. + // Obtain by calling CallAdminLogin first, then paste the token here. + AdminToken = "YOUR_ADMIN_TOKEN" + + // UserToken is the JWT token for a regular user. + // Obtain by calling CallUserLogin first, then paste the token here. + UserToken = "YOUR_USER_TOKEN" + + // TestEmail is the email address for user-related test calls. + TestEmail = "test@example.com" + + // TestPassword is the password for test user login. + TestPassword = "Test@123456" + + // AdminEmail is the email for admin login. + AdminEmail = "admin@example.com" + + // AdminPassword is the admin password. + AdminPassword = "Admin@123456" +) + +// ─── Request Options ────────────────────────────────────────────────────────── + +type reqOption func(*http.Request) + +// withToken adds a Bearer token to the request. +func withToken(token string) reqOption { + return func(r *http.Request) { + r.Header.Set("Authorization", "Bearer "+token) + } +} + +// withAdminToken adds the AdminToken. +func withAdminToken() reqOption { return withToken(AdminToken) } + +// withUserToken adds the UserToken. +func withUserToken() reqOption { return withToken(UserToken) } + +// ─── Core HTTP Helper ───────────────────────────────────────────────────────── + +// doRequest sends an HTTP request and prints the response. +// body may be nil (for GET/DELETE without body), a map[string]any, or any JSON-serialisable value. +// Returns the raw response body as a string. +func doRequest(t *testing.T, method, path string, body any, opts ...reqOption) string { + t.Helper() + + var reqBody io.Reader + if body != nil { + b, err := json.Marshal(body) + if err != nil { + t.Fatalf("marshal body: %v", err) + } + reqBody = bytes.NewReader(b) + } + + req, err := http.NewRequest(method, BaseURL+path, reqBody) + if err != nil { + t.Fatalf("new request: %v", err) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + + for _, o := range opts { + o(req) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + t.Fatalf("do request: %v", err) + } + defer resp.Body.Close() + + raw, _ := io.ReadAll(resp.Body) + result := prettyJSON(raw) + t.Logf("[%s %s] %d\n%s", method, path, resp.StatusCode, result) + return string(raw) +} + +// doRequestRaw is like doRequest but for non-testing contexts (prints to stdout). +func doRequestRaw(method, path string, body any, opts ...reqOption) string { + var reqBody io.Reader + if body != nil { + b, _ := json.Marshal(body) + reqBody = bytes.NewReader(b) + } + + req, _ := http.NewRequest(method, BaseURL+path, reqBody) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Accept", "application/json") + for _, o := range opts { + o(req) + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + fmt.Printf("request error: %v\n", err) + return "" + } + defer resp.Body.Close() + raw, _ := io.ReadAll(resp.Body) + result := prettyJSON(raw) + fmt.Printf("[%s %s] %d\n%s\n", method, path, resp.StatusCode, result) + return string(raw) +} + +func prettyJSON(raw []byte) string { + var buf bytes.Buffer + if err := json.Indent(&buf, raw, "", " "); err != nil { + return string(raw) + } + return buf.String() +}