├── .cursor └── rules │ └── global.mdc ├── .devcontainer ├── Dockerfile ├── devcontainer.json └── docker-compose.yml ├── .dockerignore ├── .env.example ├── .github └── workflows │ ├── ci-docker.yaml │ ├── ci.yaml │ ├── deploy-website.yml │ ├── docker-publish-canary.yml │ ├── docker-publish.yml │ └── reporter-release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── .release-it.json ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── apps ├── appstore-review │ ├── .gitignore │ ├── README.md │ ├── bin │ │ └── appreview.js │ ├── package.json │ ├── src │ │ ├── appstorereviews.ts │ │ ├── const.ts │ │ ├── dev.ts │ │ ├── googleplayreviews.ts │ │ ├── index.ts │ │ ├── regions.ts │ │ ├── reviews.ts │ │ ├── tianji.ts │ │ └── types.ts │ └── tsconfig.json ├── daily-ai-trigger │ ├── .editorconfig │ ├── .gitignore │ ├── .prettierrc │ ├── README.md │ ├── package.json │ ├── src │ │ ├── handler.ts │ │ ├── index.ts │ │ └── notify │ │ │ └── feishu.ts │ ├── test │ │ ├── index.spec.ts │ │ └── tsconfig.json │ ├── tsconfig.json │ ├── vitest.config.mts │ ├── worker-configuration.d.ts │ └── wrangler.jsonc └── mcp-server │ ├── .gitignore │ ├── .vscode │ ├── launch.json │ ├── mcp.json │ └── tasks.json │ ├── README.md │ ├── package.json │ ├── src │ ├── index.ts │ ├── server.ts │ └── utils │ │ └── pagination.ts │ └── tsconfig.json ├── docker-compose.yml ├── docker ├── clickhouse │ └── docker-compose.yml └── k8s │ ├── helm │ ├── .gitignore │ ├── .helmignore │ ├── Chart.lock │ ├── Chart.yaml │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── configmap.yaml │ │ ├── deployment.yaml │ │ ├── hpa.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ ├── serviceaccount.yaml │ │ └── tests │ │ │ └── test-connection.yaml │ └── values.yaml │ └── sealos │ └── tianji.yaml ├── example ├── expo │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── app.json │ ├── app │ │ ├── (tabs) │ │ │ ├── _layout.tsx │ │ │ ├── application-test.tsx │ │ │ ├── explore.tsx │ │ │ └── index.tsx │ │ ├── +not-found.tsx │ │ └── _layout.tsx │ ├── assets │ │ ├── fonts │ │ │ └── SpaceMono-Regular.ttf │ │ └── images │ │ │ ├── adaptive-icon.png │ │ │ ├── favicon.png │ │ │ ├── icon.png │ │ │ ├── partial-react-logo.png │ │ │ ├── react-logo.png │ │ │ ├── react-logo@2x.png │ │ │ ├── react-logo@3x.png │ │ │ └── splash-icon.png │ ├── babel.config.js │ ├── components │ │ ├── Collapsible.tsx │ │ ├── ExternalLink.tsx │ │ ├── HapticTab.tsx │ │ ├── HelloWave.tsx │ │ ├── ParallaxScrollView.tsx │ │ ├── ThemedText.tsx │ │ ├── ThemedView.tsx │ │ ├── __tests__ │ │ │ ├── ThemedText-test.tsx │ │ │ └── __snapshots__ │ │ │ │ └── ThemedText-test.tsx.snap │ │ └── ui │ │ │ ├── IconSymbol.ios.tsx │ │ │ ├── IconSymbol.tsx │ │ │ ├── TabBarBackground.ios.tsx │ │ │ ├── TabBarBackground.tsx │ │ │ ├── button │ │ │ └── index.tsx │ │ │ ├── form-control │ │ │ └── index.tsx │ │ │ ├── gluestack-ui-provider │ │ │ ├── config.ts │ │ │ ├── index.tsx │ │ │ ├── index.web.tsx │ │ │ ├── script.ts │ │ │ └── types.ts │ │ │ ├── hstack │ │ │ ├── index.tsx │ │ │ ├── index.web.tsx │ │ │ └── styles.tsx │ │ │ ├── input │ │ │ └── index.tsx │ │ │ ├── toast │ │ │ └── index.tsx │ │ │ └── vstack │ │ │ ├── index.tsx │ │ │ ├── index.web.tsx │ │ │ └── styles.tsx │ ├── constants │ │ └── Colors.ts │ ├── global.css │ ├── gluestack-ui.config.json │ ├── hooks │ │ ├── useColorScheme.ts │ │ ├── useColorScheme.web.ts │ │ └── useThemeColor.ts │ ├── metro.config.js │ ├── nativewind-env.d.ts │ ├── package.json │ ├── scripts │ │ └── reset-project.js │ ├── tailwind.config.js │ └── tsconfig.json └── web │ ├── .gitignore │ ├── README.md │ ├── eslint.config.mjs │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.mjs │ ├── public │ ├── file.svg │ ├── globe.svg │ ├── next.svg │ ├── vercel.svg │ └── window.svg │ ├── src │ └── app │ │ ├── favicon.ico │ │ ├── globals.css │ │ ├── layout.tsx │ │ └── page.tsx │ ├── tailwind.config.ts │ └── tsconfig.json ├── package.json ├── packages ├── client-sdk │ ├── .gitignore │ ├── package.json │ ├── src │ │ ├── application.ts │ │ ├── config.ts │ │ ├── feed.ts │ │ ├── index.ts │ │ ├── open │ │ │ ├── client │ │ │ │ ├── core │ │ │ │ │ ├── ApiError.ts │ │ │ │ │ ├── ApiRequestOptions.ts │ │ │ │ │ ├── ApiResult.ts │ │ │ │ │ ├── CancelablePromise.ts │ │ │ │ │ ├── OpenAPI.ts │ │ │ │ │ └── request.ts │ │ │ │ ├── index.ts │ │ │ │ ├── schemas.gen.ts │ │ │ │ ├── services.gen.ts │ │ │ │ └── types.gen.ts │ │ │ └── index.ts │ │ ├── survey.ts │ │ ├── tracker.spec.ts │ │ ├── tracker.ts │ │ └── types.ts │ ├── tests │ │ └── setup.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── react-native │ ├── .gitignore │ ├── package.json │ ├── src │ │ └── index.ts │ └── tsconfig.json └── react │ ├── .gitignore │ ├── package.json │ ├── src │ ├── index.ts │ └── useTianjiSurvey.ts │ └── tsconfig.json ├── patches └── zod-prisma@0.5.4.patch ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── reporter ├── go.mod ├── go.sum ├── main.go └── utils │ ├── utils.go │ └── utils_test.go ├── scripts ├── build-geo.ts ├── build-openapi-schema.ts ├── build-tracker.ts ├── fetch-llm-model.ts └── install.sh ├── src ├── client │ ├── App.tsx │ ├── api │ │ ├── authjs │ │ │ ├── lib.tsx │ │ │ ├── types.ts │ │ │ └── useAuth.ts │ │ ├── cache.ts │ │ ├── model │ │ │ ├── application.ts │ │ │ ├── monitor.ts │ │ │ ├── user.ts │ │ │ └── website.ts │ │ ├── request.ts │ │ ├── socketio.ts │ │ └── trpc.ts │ ├── components.json │ ├── components │ │ ├── AlertConfirm.tsx │ │ ├── AutoLoadingButton.tsx │ │ ├── Code.tsx │ │ ├── CodeBlock.tsx │ │ ├── CodeEditor │ │ │ ├── index.tsx │ │ │ ├── lib │ │ │ │ └── sandbox.ts │ │ │ └── main.tsx │ │ ├── CodeExample.tsx │ │ ├── ColorSchemeSwitcher.tsx │ │ ├── ColorTag.tsx │ │ ├── CommandPanel │ │ │ ├── index.tsx │ │ │ └── store.tsx │ │ ├── CommonHeader.tsx │ │ ├── CommonList.tsx │ │ ├── CommonPageEmpty.tsx │ │ ├── CommonWrapper.tsx │ │ ├── CopyableText.tsx │ │ ├── CountryFlag.tsx │ │ ├── DataRender.tsx │ │ ├── DataTable.tsx │ │ ├── DateFilter.tsx │ │ ├── DatePicker.tsx │ │ ├── DefaultError.tsx │ │ ├── DefaultNotFound.tsx │ │ ├── DelayRender.tsx │ │ ├── DeprecatedBadge.tsx │ │ ├── DevContainer.tsx │ │ ├── DialogWrapper.tsx │ │ ├── DotPatternBackground.tsx │ │ ├── DynamicVirtualList.tsx │ │ ├── EditableText.tsx │ │ ├── ErrorTip.tsx │ │ ├── FreeTierTip.tsx │ │ ├── HealthBar.tsx │ │ ├── LanguageSelector.tsx │ │ ├── Loading.tsx │ │ ├── MarkdownEditor │ │ │ ├── editor.tsx │ │ │ ├── index.tsx │ │ │ ├── plugins.ts │ │ │ ├── style.less │ │ │ ├── useLocale.ts │ │ │ └── viewer.tsx │ │ ├── MetricCard.tsx │ │ ├── MobileNavItem.tsx │ │ ├── ModalButton.tsx │ │ ├── NavItem.tsx │ │ ├── NoWorkspaceTip.tsx │ │ ├── NotFoundTip.tsx │ │ ├── PageHeader.tsx │ │ ├── ReadMore.tsx │ │ ├── SimpleVirtualList.tsx │ │ ├── Sortable │ │ │ ├── SortableContext.tsx │ │ │ ├── SortableGroup.tsx │ │ │ ├── StrictModeDroppable.tsx │ │ │ └── types.ts │ │ ├── TipIcon.tsx │ │ ├── TokenLoginContainer.tsx │ │ ├── UpDownCounter.tsx │ │ ├── UsageCard.tsx │ │ ├── VirtualizedInfiniteDataTable.tsx │ │ ├── WebhookPlayground.tsx │ │ ├── WorkspaceSwitcher.tsx │ │ ├── ai │ │ │ ├── AIPanel.tsx │ │ │ ├── store.ts │ │ │ ├── useAIAction.tsx │ │ │ └── useAIStoreContext.tsx │ │ ├── aiGateway │ │ │ ├── AIGatewayCodeExampleBtn.tsx │ │ │ ├── AIGatewayEditForm.tsx │ │ │ ├── AIGatewayLogTable.tsx │ │ │ ├── AIGatewayOverview.tsx │ │ │ ├── AIGatewayStatus.tsx │ │ │ └── useAIGatewayLogColumns.tsx │ │ ├── application │ │ │ ├── AppStoreSearchInput.tsx │ │ │ ├── ApplicationCompareChart.tsx │ │ │ ├── ApplicationCompareTab.tsx │ │ │ ├── ApplicationDetailCard.tsx │ │ │ ├── ApplicationEditForm.tsx │ │ │ ├── ApplicationOverviewCard.tsx │ │ │ ├── ApplicationStatsChart.tsx │ │ │ └── StatCard.tsx │ │ ├── billing │ │ │ ├── SubscriptionSelection.tsx │ │ │ └── usePlans.tsx │ │ ├── chart │ │ │ ├── SimplePieChart.tsx │ │ │ └── TimeEventChart.tsx │ │ ├── feed │ │ │ ├── FeedApiGuide.tsx │ │ │ ├── FeedArchivePageButton.tsx │ │ │ ├── FeedChannelEditForm.tsx │ │ │ ├── FeedChannelPicker.tsx │ │ │ ├── FeedEventItem.tsx │ │ │ ├── FeedIcon.tsx │ │ │ ├── FeedIntegration.tsx │ │ │ └── FeedStateList.tsx │ │ ├── insights │ │ │ ├── BreakdownParamsBlock.tsx │ │ │ ├── BreakdownSection.tsx │ │ │ ├── ChartRender.tsx │ │ │ ├── ChartTypeSelection.tsx │ │ │ ├── DataTypeIcon.tsx │ │ │ ├── DateRangeSelection.tsx │ │ │ ├── DateUnitSelection.tsx │ │ │ ├── DropdownSelect.tsx │ │ │ ├── FilterParamsBlock.tsx │ │ │ ├── FilterParamsOperator.tsx │ │ │ ├── FilterSection.tsx │ │ │ ├── MetricsBlock.tsx │ │ │ ├── MetricsSection.tsx │ │ │ ├── TableView.tsx │ │ │ └── utils │ │ │ │ └── filterOperator.tsx │ │ ├── layout │ │ │ ├── DesktopLayout.tsx │ │ │ ├── Header.tsx │ │ │ ├── Menu.tsx │ │ │ ├── MobileLayout.tsx │ │ │ ├── Nav.tsx │ │ │ ├── UserConfig.tsx │ │ │ ├── index.tsx │ │ │ └── types.ts │ │ ├── loading │ │ │ ├── Searching.tsx │ │ │ └── searching.json │ │ ├── modals │ │ │ └── NotificationInfo │ │ │ │ ├── index.tsx │ │ │ │ └── strategies │ │ │ │ ├── apprise.tsx │ │ │ │ ├── feishu.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── smtp.tsx │ │ │ │ ├── teams.tsx │ │ │ │ ├── telegram.tsx │ │ │ │ └── webhook.tsx │ │ ├── monitor │ │ │ ├── CustomizedErrorArea.tsx │ │ │ ├── MonitorBadgeView.tsx │ │ │ ├── MonitorDataChart.tsx │ │ │ ├── MonitorDataMetrics.tsx │ │ │ ├── MonitorEventList.tsx │ │ │ ├── MonitorHealthBar.tsx │ │ │ ├── MonitorInfo.tsx │ │ │ ├── MonitorInfoEditor.tsx │ │ │ ├── MonitorList.tsx │ │ │ ├── MonitorListItem.tsx │ │ │ ├── MonitorPicker.tsx │ │ │ ├── MonitorPublicDataChart.tsx │ │ │ ├── MonitorPushStatus.tsx │ │ │ ├── MonitorStatsBlock.tsx │ │ │ ├── PushMonitorUsageModal.tsx │ │ │ ├── StatusPage │ │ │ │ ├── Body.tsx │ │ │ │ ├── EditForm.tsx │ │ │ │ ├── ServiceList.tsx │ │ │ │ ├── Services.tsx │ │ │ │ ├── StatusHeader.tsx │ │ │ │ ├── const.ts │ │ │ │ ├── index.tsx │ │ │ │ ├── schema.ts │ │ │ │ ├── store.ts │ │ │ │ └── useAllowEdit.ts │ │ │ ├── provider │ │ │ │ ├── custom.tsx │ │ │ │ ├── dns.tsx │ │ │ │ ├── http.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── openai.tsx │ │ │ │ ├── ping.tsx │ │ │ │ ├── push.tsx │ │ │ │ ├── tcp.tsx │ │ │ │ └── types.ts │ │ │ └── useMonitorAction.ts │ │ ├── notification │ │ │ └── NotificationPicker.tsx │ │ ├── server │ │ │ ├── AddServerStep.tsx │ │ │ ├── ColorizedText.tsx │ │ │ ├── InstallScript.tsx │ │ │ ├── ServerList.tsx │ │ │ ├── ServerRowExpendView.tsx │ │ │ └── useServerMap.ts │ │ ├── survey │ │ │ ├── SurveyAIBtn │ │ │ │ ├── SurveyAISummary.tsx │ │ │ │ ├── SurveyAITranslation.tsx │ │ │ │ └── index.tsx │ │ │ ├── SurveyCategoryChart.tsx │ │ │ ├── SurveyDownloadBtn.tsx │ │ │ ├── SurveyEditForm.tsx │ │ │ ├── SurveyListTable.tsx │ │ │ ├── SurveyUsageBtn.tsx │ │ │ └── useSurveyListColumns.tsx │ │ ├── telemetry │ │ │ ├── TelemetryMetricsTable.tsx │ │ │ └── TelemetryOverview.tsx │ │ ├── ui │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── chart.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── menubar.tsx │ │ │ ├── popover.tsx │ │ │ ├── progress.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── sonner.tsx │ │ │ ├── spinner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ └── tooltip.tsx │ │ ├── website │ │ │ ├── WebsiteCodeBtn.tsx │ │ │ ├── WebsiteConfig.tsx │ │ │ ├── WebsiteLighthouseBtn.tsx │ │ │ ├── WebsiteMetricsTable.tsx │ │ │ ├── WebsiteOnlineCount.tsx │ │ │ ├── WebsiteOverview.tsx │ │ │ ├── WebsiteVisitorMap │ │ │ │ ├── VisitorLarkMap.tsx │ │ │ │ ├── VisitorLeafletMap.css │ │ │ │ ├── VisitorLeafletMap.tsx │ │ │ │ ├── index.tsx │ │ │ │ └── utils.ts │ │ │ └── WebsiteVisitorMapBtn.tsx │ │ └── workspace │ │ │ ├── WorkspacePauseTip.tsx │ │ │ └── useWorkspaceMembers.tsx │ ├── global.d.ts │ ├── hooks │ │ ├── useConfig.ts │ │ ├── useDataReady.ts │ │ ├── useEvent.ts │ │ ├── useFuseSearch.ts │ │ ├── useGlobalRangeDate.tsx │ │ ├── useInjectWebsiteScript.ts │ │ ├── useIntervalUpdate.ts │ │ ├── useIsMobile.ts │ │ ├── useRequest.ts │ │ ├── useResizeObserver.ts │ │ ├── useStrokeDasharray.ts │ │ ├── useTheme.ts │ │ ├── useVisibilityChange.ts │ │ ├── useWatch.ts │ │ └── useWindowSize.ts │ ├── i18next-toolkit.config.cjs │ ├── index.css │ ├── index.html │ ├── init.ts │ ├── main.tsx │ ├── package.json │ ├── postcss.config.cts │ ├── public │ │ ├── favicon.ico │ │ ├── icon.svg │ │ ├── locales │ │ │ ├── de-DE │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── en │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── fr-FR │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── ja-JP │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── pl-PL │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── pt-PT │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ ├── ru-RU │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ │ └── zh-CN │ │ │ │ ├── flag.png │ │ │ │ └── translation.json │ │ └── vite.svg │ ├── routeTree.gen.ts │ ├── routes │ │ ├── __root.tsx │ │ ├── aiGateway.tsx │ │ ├── aiGateway │ │ │ ├── $gatewayId │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── application.tsx │ │ ├── application │ │ │ ├── $applicationId │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ ├── compare.tsx │ │ │ ├── index.tsx │ │ │ └── overview.tsx │ │ ├── feed.tsx │ │ ├── feed │ │ │ ├── $channelId │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── feed_ │ │ │ └── playground.tsx │ │ ├── index.tsx │ │ ├── insights │ │ │ ├── events.tsx │ │ │ └── index.tsx │ │ ├── invitation │ │ │ └── accept │ │ │ │ └── $token.tsx │ │ ├── login.tsx │ │ ├── monitor.tsx │ │ ├── monitor │ │ │ ├── $monitorId │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── page.tsx │ │ ├── page │ │ │ ├── $slug.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── playground.tsx │ │ ├── register.tsx │ │ ├── server.tsx │ │ ├── settings.tsx │ │ ├── settings │ │ │ ├── apiKey.tsx │ │ │ ├── auditLog.tsx │ │ │ ├── billing.tsx │ │ │ ├── notifications.tsx │ │ │ ├── profile.tsx │ │ │ ├── usage.tsx │ │ │ └── workspace.tsx │ │ ├── status │ │ │ └── $slug.tsx │ │ ├── survey.tsx │ │ ├── survey │ │ │ ├── $surveyId │ │ │ │ ├── edit.tsx │ │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── switchWorkspace.tsx │ │ ├── telemetry.tsx │ │ ├── telemetry │ │ │ ├── $telemetryId.tsx │ │ │ ├── add.tsx │ │ │ └── index.tsx │ │ ├── website.tsx │ │ └── website │ │ │ ├── $websiteId │ │ │ ├── config.tsx │ │ │ └── index.tsx │ │ │ ├── add.tsx │ │ │ └── overview.tsx │ ├── store │ │ ├── global.ts │ │ ├── insights.ts │ │ ├── settings.ts │ │ └── user.ts │ ├── styles │ │ └── global.less │ ├── tailwind.config.ts │ ├── tsconfig.json │ ├── utils │ │ ├── __snapshots__ │ │ │ ├── date.spec.ts.snap │ │ │ └── survey.spec.ts.snap │ │ ├── color.ts │ │ ├── common.ts │ │ ├── country.ts │ │ ├── date.spec.ts │ │ ├── date.ts │ │ ├── dom.ts │ │ ├── env.ts │ │ ├── error.ts │ │ ├── event.ts │ │ ├── health.spec.ts │ │ ├── health.ts │ │ ├── i18n.ts │ │ ├── reorder.spec.ts │ │ ├── reorder.ts │ │ ├── route.ts │ │ ├── style.ts │ │ ├── survey.spec.ts │ │ ├── survey.ts │ │ ├── type.ts │ │ ├── url.spec.ts │ │ ├── url.ts │ │ ├── validator.spec.ts │ │ └── validator.ts │ ├── vite-env.d.ts │ └── vite.config.ts ├── server │ ├── .gitignore │ ├── app.ts │ ├── cache │ │ └── index.ts │ ├── cronjob │ │ ├── daily.spec.ts │ │ ├── daily.ts │ │ ├── index.spec.ts │ │ ├── index.ts │ │ └── shared.ts │ ├── init.ts │ ├── main.ts │ ├── middleware │ │ ├── auth.ts │ │ ├── prometheus │ │ │ ├── README.md │ │ │ ├── index.ts │ │ │ ├── middleware.ts │ │ │ ├── types.ts │ │ │ └── utils.ts │ │ ├── validate.ts │ │ └── workspace.ts │ ├── model │ │ ├── _client.ts │ │ ├── _schema │ │ │ ├── __tests__ │ │ │ │ └── feed.spec.ts │ │ │ ├── feed.ts │ │ │ ├── filter.ts │ │ │ ├── index.ts │ │ │ └── monitor.ts │ │ ├── aiGateway.ts │ │ ├── application │ │ │ ├── event.ts │ │ │ ├── index.ts │ │ │ └── storeInfo.ts │ │ ├── auditLog.ts │ │ ├── auth.ts │ │ ├── billing │ │ │ ├── credit.ts │ │ │ ├── cronjob.ts │ │ │ ├── index.ts │ │ │ ├── limit.ts │ │ │ └── workspace.ts │ │ ├── feed │ │ │ ├── event.ts │ │ │ ├── shared.ts │ │ │ └── state.ts │ │ ├── insights │ │ │ ├── __snapshots__ │ │ │ │ ├── aiGateway.spec.ts.snap │ │ │ │ ├── survey.spec.ts.snap │ │ │ │ ├── utils.spec.ts.snap │ │ │ │ └── website.spec.ts.snap │ │ │ ├── aiGateway.spec.ts │ │ │ ├── aiGateway.ts │ │ │ ├── index.ts │ │ │ ├── shared.ts │ │ │ ├── survey.spec.ts │ │ │ ├── survey.ts │ │ │ ├── utils.spec.ts │ │ │ ├── utils.ts │ │ │ ├── website.spec.ts │ │ │ └── website.ts │ │ ├── invitation.ts │ │ ├── monitor │ │ │ ├── index.bench.ts │ │ │ ├── index.ts │ │ │ ├── manager.ts │ │ │ ├── page │ │ │ │ └── manager.ts │ │ │ ├── provider │ │ │ │ ├── __tests__ │ │ │ │ │ ├── dns.spec.ts │ │ │ │ │ └── openai.spec.ts │ │ │ │ ├── _utils.ts │ │ │ │ ├── custom.ts │ │ │ │ ├── dns.ts │ │ │ │ ├── http.ts │ │ │ │ ├── index.ts │ │ │ │ ├── openai.ts │ │ │ │ ├── ping.ts │ │ │ │ ├── push.ts │ │ │ │ ├── tcp.ts │ │ │ │ └── type.ts │ │ │ ├── runner.ts │ │ │ └── types.ts │ │ ├── notification │ │ │ ├── index.ts │ │ │ ├── provider │ │ │ │ ├── __tests__ │ │ │ │ │ ├── apprise.spec.ts │ │ │ │ │ └── feishu.spec.ts │ │ │ │ ├── apprise.ts │ │ │ │ ├── feishu.ts │ │ │ │ ├── index.ts │ │ │ │ ├── smtp.ts │ │ │ │ ├── teams.ts │ │ │ │ ├── telegram.ts │ │ │ │ ├── type.ts │ │ │ │ └── webhook.ts │ │ │ └── token │ │ │ │ ├── index.ts │ │ │ │ ├── tokenizer.test.ts │ │ │ │ ├── tokenizer │ │ │ │ ├── base.ts │ │ │ │ ├── html.ts │ │ │ │ ├── markdown.ts │ │ │ │ └── telegram.ts │ │ │ │ └── type.ts │ │ ├── openai.spec.ts │ │ ├── openai.ts │ │ ├── prompt │ │ │ ├── shared │ │ │ │ ├── tools.spec.ts │ │ │ │ ├── tools.ts │ │ │ │ └── utils.ts │ │ │ ├── survey.spec.ts │ │ │ └── survey.ts │ │ ├── serverStatus.ts │ │ ├── survey.ts │ │ ├── task.ts │ │ ├── telemetry.ts │ │ ├── user.ts │ │ ├── website │ │ │ └── index.ts │ │ └── workspace.ts │ ├── mq │ │ ├── producer.ts │ │ ├── shared.ts │ │ └── worker.ts │ ├── package.json │ ├── prisma │ │ ├── migrations │ │ │ ├── 20231021072359_init │ │ │ │ └── migration.sql │ │ │ ├── 20231024085853_add_monitor_status │ │ │ │ └── migration.sql │ │ │ ├── 20231031143357_add_telemetry_event_payload │ │ │ │ └── migration.sql │ │ │ ├── 20231112152240_add_dashboard_layout │ │ │ │ └── migration.sql │ │ │ ├── 20231127154808_add_telemetry_session_ip │ │ │ │ └── migration.sql │ │ │ ├── 20231213113426_add_monitor_status_page │ │ │ │ └── migration.sql │ │ │ ├── 20231229130639_add_monitor_updated_time │ │ │ │ └── migration.sql │ │ │ ├── 20240105145715_add_workspace_daily_usage │ │ │ │ └── migration.sql │ │ │ ├── 20240109123840_add_monitor_data_created_at_index │ │ │ │ └── migration.sql │ │ │ ├── 20240122041756_add_workspace_audit_log_and_settings │ │ │ │ └── migration.sql │ │ │ ├── 20240129091132_add_ip_location_info │ │ │ │ └── migration.sql │ │ │ ├── 20240209124945_add_monitor_maxretries │ │ │ │ └── migration.sql │ │ │ ├── 20240217104338_add_named_telemetry_list │ │ │ │ └── migration.sql │ │ │ ├── 20240417150948_add_status_page_domain │ │ │ │ └── migration.sql │ │ │ ├── 20240423074833_add_indexes_for_monitor │ │ │ │ └── migration.sql │ │ │ ├── 20240427130848_add_survey │ │ │ │ └── migration.sql │ │ │ ├── 20240502082154_add_monitor_trending_mode │ │ │ │ └── migration.sql │ │ │ ├── 20240622115055_add_feed_channel_and_event │ │ │ │ └── migration.sql │ │ │ ├── 20240624151129_add_feed_event_url │ │ │ │ └── migration.sql │ │ │ ├── 20240713071153_add_feed_channel_notification │ │ │ │ └── migration.sql │ │ │ ├── 20240713082513_update_feed_channel_frequency_to_enum │ │ │ │ └── migration.sql │ │ │ ├── 20240727132608_add_none_notify_frequency │ │ │ │ └── migration.sql │ │ │ ├── 20240729165431_add_authjs_models │ │ │ │ └── migration.sql │ │ │ ├── 20240804094220_add_survey_and_feed_count │ │ │ │ └── migration.sql │ │ │ ├── 20240813163057_add_survey_feedchannelids │ │ │ │ └── migration.sql │ │ │ ├── 20240814145845_add_survey_feed_template │ │ │ │ └── migration.sql │ │ │ ├── 20240830162007_add_feed_event_archived_field │ │ │ │ └── migration.sql │ │ │ ├── 20240913094805_add_website_lh_report │ │ │ │ └── migration.sql │ │ │ ├── 20240914125927_remove_user_current_workspace_id_fk │ │ │ │ └── migration.sql │ │ │ ├── 20240916135644_add_monitor_status_page_body │ │ │ │ └── migration.sql │ │ │ ├── 20240921130512_add_statuspage_incident │ │ │ │ └── migration.sql │ │ │ ├── 20240930172940_add_lighthouse_score │ │ │ │ └── migration.sql │ │ │ ├── 20240930174147_add_lighthouse_errormessage │ │ │ │ └── migration.sql │ │ │ ├── 20241003005451_add_lemon_squeezy │ │ │ │ └── migration.sql │ │ │ ├── 20241003134353_add_feed_event_payload │ │ │ │ └── migration.sql │ │ │ ├── 20241007083106_add_workspace_subscription │ │ │ │ └── migration.sql │ │ │ ├── 20241010154732_add_survey_webhook │ │ │ │ └── migration.sql │ │ │ ├── 20241028184601_add_feedchannel_webhook_signature │ │ │ │ └── migration.sql │ │ │ ├── 20241103091612_add_user_api_key │ │ │ │ └── migration.sql │ │ │ ├── 20241103103959_add_api_key_usage │ │ │ │ └── migration.sql │ │ │ ├── 20241106143832_add_workspace_paused_field │ │ │ │ └── migration.sql │ │ │ ├── 20241116103531_add_monitor_recent_error │ │ │ │ └── migration.sql │ │ │ ├── 20241230164830_add_workspace_bill_and_credit │ │ │ │ └── migration.sql │ │ │ ├── 20250130110404_add_key_in_sessiondate │ │ │ │ └── migration.sql │ │ │ ├── 20250207162041_remove_bill_workspace_id │ │ │ │ └── migration.sql │ │ │ ├── 20250207163235_add_survey_ai_category │ │ │ │ └── migration.sql │ │ │ ├── 20250220152350_add_workspace_task │ │ │ │ └── migration.sql │ │ │ ├── 20250301131016_add_survey_ai_translation │ │ │ │ └── migration.sql │ │ │ ├── 20250308085226_add_task_audio_type │ │ │ │ └── migration.sql │ │ │ ├── 20250315145755_add_application_models │ │ │ │ └── migration.sql │ │ │ ├── 20250315164747_remove_storeinfo_limit │ │ │ │ └── migration.sql │ │ │ ├── 20250320090258_add_application_event_info │ │ │ │ └── migration.sql │ │ │ ├── 20250323094200_add_application_store_history │ │ │ │ └── migration.sql │ │ │ ├── 20250405120442_add_workspace_invitation │ │ │ │ └── migration.sql │ │ │ ├── 20250406132123_add_aigateway_model │ │ │ │ └── migration.sql │ │ │ ├── 20250407145403_add_aigateway_price │ │ │ │ └── migration.sql │ │ │ ├── 20250519091903_add_feed_state │ │ │ │ └── migration.sql │ │ │ ├── 20250521172806_add_userapi_desc │ │ │ │ └── migration.sql │ │ │ ├── 20250530154022_add_aigateway_user_apikey │ │ │ │ └── migration.sql │ │ │ └── migration_lock.toml │ │ ├── schema.prisma │ │ ├── scripts │ │ │ └── populate-ip-location.ts │ │ └── zod │ │ │ ├── account.ts │ │ │ ├── aigateway.ts │ │ │ ├── aigatewaylogs.ts │ │ │ ├── application.ts │ │ │ ├── applicationevent.ts │ │ │ ├── applicationeventdata.ts │ │ │ ├── applicationsession.ts │ │ │ ├── applicationsessiondata.ts │ │ │ ├── applicationstoreinfo.ts │ │ │ ├── applicationstoreinfohistory.ts │ │ │ ├── feedchannel.ts │ │ │ ├── feedevent.ts │ │ │ ├── feedstate.ts │ │ │ ├── index.ts │ │ │ ├── lemonsqueezysubscription.ts │ │ │ ├── lemonsqueezywebhookevent.ts │ │ │ ├── monitor.ts │ │ │ ├── monitordata.ts │ │ │ ├── monitorevent.ts │ │ │ ├── monitorstatus.ts │ │ │ ├── monitorstatuspage.ts │ │ │ ├── notification.ts │ │ │ ├── schemas │ │ │ └── index.ts │ │ │ ├── session.ts │ │ │ ├── statuspageincident.ts │ │ │ ├── survey.ts │ │ │ ├── surveyresult.ts │ │ │ ├── telemetry.ts │ │ │ ├── telemetryevent.ts │ │ │ ├── telemetrysession.ts │ │ │ ├── user.ts │ │ │ ├── userapikey.ts │ │ │ ├── verificationtoken.ts │ │ │ ├── website.ts │ │ │ ├── websiteevent.ts │ │ │ ├── websiteeventdata.ts │ │ │ ├── websitelighthousereport.ts │ │ │ ├── websitesession.ts │ │ │ ├── websitesessiondata.ts │ │ │ ├── workspace.ts │ │ │ ├── workspaceauditlog.ts │ │ │ ├── workspacebill.ts │ │ │ ├── workspacedailyusage.ts │ │ │ ├── workspaceinvitation.ts │ │ │ ├── workspacesonusers.ts │ │ │ ├── workspacesubscription.ts │ │ │ └── workspacetask.ts │ ├── router │ │ ├── __test__ │ │ │ └── telemetry.test.ts │ │ ├── aiGateway.ts │ │ ├── application.ts │ │ ├── billing.ts │ │ ├── health.ts │ │ ├── lighthouse.ts │ │ ├── monitor.ts │ │ ├── push.ts │ │ ├── serverStatus.ts │ │ ├── telemetry.ts │ │ └── website.ts │ ├── tests │ │ ├── seeds │ │ │ └── website.ts │ │ ├── utils.ts │ │ └── vm │ │ │ └── isolated-vm.ts │ ├── trpc │ │ ├── index.ts │ │ ├── routers │ │ │ ├── ai.ts │ │ │ ├── aiGateway.ts │ │ │ ├── application.ts │ │ │ ├── auditLog.ts │ │ │ ├── billing.ts │ │ │ ├── feed │ │ │ │ ├── index.ts │ │ │ │ ├── integration.ts │ │ │ │ └── state.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── insights.ts │ │ │ ├── monitor.ts │ │ │ ├── notification.ts │ │ │ ├── serverStatus.ts │ │ │ ├── survey.ts │ │ │ ├── telemetry.ts │ │ │ ├── user.ts │ │ │ ├── website.ts │ │ │ └── workspace.ts │ │ └── trpc.ts │ ├── tsconfig.json │ ├── types │ │ ├── @auth │ │ │ └── express │ │ │ │ └── index.d.ts │ │ ├── global.d.ts │ │ ├── prisma.ts │ │ └── utils.ts │ ├── udp │ │ └── server.ts │ ├── utils │ │ ├── __tests__ │ │ │ ├── common.test.ts │ │ │ ├── detect.test.ts │ │ │ └── smtp.test.ts │ │ ├── cast.ts │ │ ├── common.ts │ │ ├── const.ts │ │ ├── detect.ts │ │ ├── email-template-example.html │ │ ├── env.ts │ │ ├── json.ts │ │ ├── lib.ts │ │ ├── llm.ts │ │ ├── logger.ts │ │ ├── model_prices_and_context_window.json │ │ ├── prisma.ts │ │ ├── prometheus │ │ │ ├── client.ts │ │ │ └── index.ts │ │ ├── schema.ts │ │ ├── screenshot │ │ │ ├── lighthouse.ts │ │ │ └── website.ts │ │ ├── smtp.ts │ │ ├── template.ts │ │ ├── types.ts │ │ └── vm │ │ │ ├── index.ts │ │ │ ├── sandbox-vm2.ts │ │ │ └── sandbox.ts │ └── ws │ │ ├── index.ts │ │ └── shared.ts ├── shared │ ├── package.json │ ├── src │ │ ├── config.ts │ │ ├── date.spec.ts │ │ ├── date.ts │ │ ├── index.ts │ │ ├── insights.ts │ │ ├── regex.spec.ts │ │ ├── regex.ts │ │ ├── role.ts │ │ └── server.ts │ ├── tsconfig.json │ └── tsup.config.ts ├── tracker │ └── index.js └── types │ ├── index.ts │ ├── monitor.ts │ ├── server.ts │ └── utils.ts ├── tsconfig.base.json └── website ├── .gitignore ├── README.md ├── babel.config.js ├── blog ├── 2025-04-08-how-to-ensure-9999-uptime-for-your-website-with-proactive-monitoring.md └── 2025-04-09-top-metrics-to-track-for-effective-website-analytics-in-2025.md ├── components.json ├── docs ├── api │ ├── _category_.json │ ├── authentication.md │ ├── getting-started.md │ └── openapi-sdk.md ├── application │ ├── _category_.json │ └── tracking.md ├── dev │ ├── _category_.json │ └── prepare.md ├── environment.md ├── events │ ├── _category_.json │ └── track.md ├── feed │ ├── _category_.json │ └── sentry.md ├── install │ ├── _category_.json │ ├── environment.md │ ├── kubernetes │ │ ├── _category_.json │ │ └── helm.md │ ├── manual.md │ ├── other │ │ ├── _category_.json │ │ └── install-in-aapanel.md │ └── telemetry.md ├── intro.md ├── mcp │ ├── _category_.json │ └── mcp.md ├── monitor │ ├── _category_.json │ ├── custom-script.md │ └── push-monitor.md ├── notification │ ├── _category_.json │ └── webhook.md ├── server-status │ ├── _category_.json │ ├── server-status-page.md │ └── server-status-reporter.md ├── special-thanks.md ├── telemetry │ ├── _category_.json │ ├── intro.md │ └── params.md └── website │ ├── _category_.json │ ├── framework │ ├── _category_.json │ └── docusaurus.md │ └── track-script.md ├── docusaurus.config.ts ├── i18n ├── de │ ├── code.json │ ├── docusaurus-plugin-content-blog │ │ └── options.json │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── api │ │ │ ├── authentication.md │ │ │ ├── getting-started.md │ │ │ └── openapi-sdk.md │ │ │ ├── application │ │ │ └── tracking.md │ │ │ ├── dev │ │ │ └── prepare.md │ │ │ ├── environment.md │ │ │ ├── events │ │ │ └── track.md │ │ │ ├── feed │ │ │ └── sentry.md │ │ │ ├── install │ │ │ ├── environment.md │ │ │ ├── kubernetes │ │ │ │ └── helm.md │ │ │ ├── manual.md │ │ │ ├── other │ │ │ │ └── install-in-aapanel.md │ │ │ └── telemetry.md │ │ │ ├── intro.md │ │ │ ├── mcp │ │ │ └── mcp.md │ │ │ ├── monitor │ │ │ ├── custom-script.md │ │ │ └── push-monitor.md │ │ │ ├── notification │ │ │ └── webhook.md │ │ │ ├── server-status │ │ │ ├── server-status-page.md │ │ │ └── server-status-reporter.md │ │ │ ├── special-thanks.md │ │ │ ├── telemetry │ │ │ ├── intro.md │ │ │ └── params.md │ │ │ └── website │ │ │ ├── framework │ │ │ └── docusaurus.md │ │ │ └── track-script.md │ └── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json ├── fr │ ├── code.json │ ├── docusaurus-plugin-content-blog │ │ └── options.json │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── api │ │ │ ├── authentication.md │ │ │ ├── getting-started.md │ │ │ └── openapi-sdk.md │ │ │ ├── application │ │ │ └── tracking.md │ │ │ ├── dev │ │ │ └── prepare.md │ │ │ ├── environment.md │ │ │ ├── events │ │ │ └── track.md │ │ │ ├── feed │ │ │ └── sentry.md │ │ │ ├── install │ │ │ ├── environment.md │ │ │ ├── kubernetes │ │ │ │ └── helm.md │ │ │ ├── manual.md │ │ │ ├── other │ │ │ │ └── install-in-aapanel.md │ │ │ └── telemetry.md │ │ │ ├── intro.md │ │ │ ├── mcp │ │ │ └── mcp.md │ │ │ ├── monitor │ │ │ ├── custom-script.md │ │ │ └── push-monitor.md │ │ │ ├── notification │ │ │ └── webhook.md │ │ │ ├── server-status │ │ │ ├── server-status-page.md │ │ │ └── server-status-reporter.md │ │ │ ├── special-thanks.md │ │ │ ├── telemetry │ │ │ ├── intro.md │ │ │ └── params.md │ │ │ └── website │ │ │ ├── framework │ │ │ └── docusaurus.md │ │ │ └── track-script.md │ └── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json ├── ja │ ├── code.json │ ├── docusaurus-plugin-content-blog │ │ └── options.json │ ├── docusaurus-plugin-content-docs │ │ ├── current.json │ │ └── current │ │ │ ├── api │ │ │ ├── authentication.md │ │ │ ├── getting-started.md │ │ │ └── openapi-sdk.md │ │ │ ├── application │ │ │ └── tracking.md │ │ │ ├── dev │ │ │ └── prepare.md │ │ │ ├── environment.md │ │ │ ├── events │ │ │ └── track.md │ │ │ ├── feed │ │ │ └── sentry.md │ │ │ ├── install │ │ │ ├── environment.md │ │ │ ├── kubernetes │ │ │ │ └── helm.md │ │ │ ├── manual.md │ │ │ ├── other │ │ │ │ └── install-in-aapanel.md │ │ │ └── telemetry.md │ │ │ ├── intro.md │ │ │ ├── mcp │ │ │ └── mcp.md │ │ │ ├── monitor │ │ │ ├── custom-script.md │ │ │ └── push-monitor.md │ │ │ ├── notification │ │ │ └── webhook.md │ │ │ ├── server-status │ │ │ ├── server-status-page.md │ │ │ └── server-status-reporter.md │ │ │ ├── special-thanks.md │ │ │ ├── telemetry │ │ │ ├── intro.md │ │ │ └── params.md │ │ │ └── website │ │ │ ├── framework │ │ │ └── docusaurus.md │ │ │ └── track-script.md │ └── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json └── zh-Hans │ ├── code.json │ ├── docusaurus-plugin-content-blog │ └── options.json │ ├── docusaurus-plugin-content-docs │ ├── current.json │ └── current │ │ ├── api │ │ ├── authentication.md │ │ ├── getting-started.md │ │ └── openapi-sdk.md │ │ ├── application │ │ └── tracking.md │ │ ├── dev │ │ └── prepare.md │ │ ├── environment.md │ │ ├── events │ │ └── track.md │ │ ├── feed │ │ └── sentry.md │ │ ├── install │ │ ├── environment.md │ │ ├── kubernetes │ │ │ └── helm.md │ │ ├── manual.md │ │ ├── other │ │ │ └── install-in-aapanel.md │ │ └── telemetry.md │ │ ├── intro.md │ │ ├── mcp │ │ └── mcp.md │ │ ├── monitor │ │ ├── custom-script.md │ │ └── push-monitor.md │ │ ├── notification │ │ └── webhook.md │ │ ├── server-status │ │ ├── server-status-page.md │ │ └── server-status-reporter.md │ │ ├── special-thanks.md │ │ ├── telemetry │ │ ├── intro.md │ │ └── params.md │ │ └── website │ │ ├── framework │ │ └── docusaurus.md │ │ └── track-script.md │ └── docusaurus-theme-classic │ ├── footer.json │ └── navbar.json ├── openapi.json ├── package.json ├── postcss.config.js ├── sidebars.js ├── src ├── components │ ├── BlockCard.tsx │ ├── HomepageFeatures │ │ ├── index.tsx │ │ └── styles.module.css │ ├── homepage │ │ ├── Features.tsx │ │ ├── Flags.tsx │ │ ├── HeaderLight.tsx │ │ └── SimpleLight.tsx │ └── ui │ │ ├── border-beam.tsx │ │ ├── client-tweet-card.tsx │ │ ├── interactive-hover-button.tsx │ │ ├── shimmer-button.tsx │ │ ├── shine-border.tsx │ │ └── tweet-card.tsx ├── css │ ├── base.css │ └── custom.css ├── lib │ └── utils.ts └── pages │ ├── changelog.tsx │ ├── index.css │ ├── index.tsx │ ├── markdown-page.md │ ├── pricing.tsx │ └── private-policy.md ├── static ├── .nojekyll ├── img │ ├── dark-brand.svg │ ├── docs │ │ ├── aapanel │ │ │ ├── 1.png │ │ │ ├── 1_cn.png │ │ │ ├── 2.png │ │ │ └── 2_cn.png │ │ └── sentry │ │ │ ├── sentry1.png │ │ │ ├── sentry2.png │ │ │ ├── sentry3.png │ │ │ ├── sentry4.png │ │ │ ├── sentry5.png │ │ │ └── sentry6.png │ ├── docusaurus-social-card.jpg │ ├── docusaurus.png │ ├── favicon.ico │ ├── flags │ │ ├── de.png │ │ ├── en.png │ │ ├── fr.png │ │ ├── jp.png │ │ ├── pl.png │ │ ├── pt.png │ │ ├── ru.png │ │ └── zh.png │ ├── gdpr-ccpa.svg │ ├── logo.svg │ ├── logo@128.png │ ├── no-cookie-track.svg │ ├── openapi-connect-with-developer.svg │ ├── preview │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ ├── preview1.png │ ├── preview2.png │ ├── preview3.png │ ├── preview4.png │ ├── social-card.png │ ├── social │ │ ├── github.png │ │ ├── github.svg │ │ ├── x.png │ │ └── x.svg │ ├── telemetry │ │ ├── 1.png │ │ └── 2.png │ ├── undraw_docusaurus_mountain.svg │ ├── undraw_docusaurus_react.svg │ ├── undraw_docusaurus_tree.svg │ └── wechat.jpg ├── llms-full.txt ├── llms.txt ├── robots.txt └── yandex_a2d32275c07c6d98.html ├── tailwind.config.ts └── tsconfig.json /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/javascript-node:1-18-bullseye 2 | 3 | # [Optional] Uncomment this section to install additional OS packages. 4 | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 5 | && apt-get -y install --no-install-recommends iputils-ping 6 | 7 | # [Optional] Uncomment if you want to install an additional version of node using nvm 8 | ARG EXTRA_NODE_VERSION=18 9 | RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" 10 | 11 | # [Optional] Uncomment if you want to install more global node modules 12 | # RUN su node -c "npm install -g " 13 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | apps 2 | example 3 | docker 4 | node_modules 5 | packages 6 | website 7 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # postgresql url 2 | DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public" 3 | 4 | # Whether allow feature 5 | ALLOW_REGISTER=false 6 | ALLOW_OPENAPI=true 7 | 8 | # For analyze tianji self 9 | WEBSITE_ID= 10 | 11 | # For secury 12 | JWT_SECRET=replace-with-random-string # default is `daily string` 13 | JWT_ISSUER= # default is `tianji.msgbyte.com` 14 | JWT_AUDIENCE= # default is `msgbyte.com` 15 | -------------------------------------------------------------------------------- /.github/workflows/deploy-website.yml: -------------------------------------------------------------------------------- 1 | name: "Deployment Website" 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | paths: 8 | - "website/**" 9 | workflow_dispatch: 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | node-version: [18.x] 17 | defaults: 18 | run: 19 | working-directory: website 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Deploy to Vercel 23 | uses: amondnet/vercel-action@v25.1.1 24 | env: 25 | VERSION: ${{ env.GITHUB_SHA }} 26 | with: 27 | vercel-token: ${{ secrets.VERCEL_TOKEN }} 28 | vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} 29 | vercel-project-id: prj_V3iY3AcH0wIXxMHW4oDy3W3XzfnN 30 | working-directory: ./ 31 | vercel-args: '--prod' 32 | -------------------------------------------------------------------------------- /.github/workflows/reporter-release.yml: -------------------------------------------------------------------------------- 1 | name: Build Reporter Release 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | build-go-binary: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | matrix: 12 | goos: [linux, windows, darwin] 13 | goarch: [amd64, arm64] 14 | exclude: 15 | - goarch: arm64 16 | goos: windows 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: wangyoucao577/go-release-action@v1.40 20 | with: 21 | github_token: ${{ secrets.GITHUB_TOKEN }} 22 | project_path: ./reporter 23 | goos: ${{ matrix.goos }} 24 | goarch: ${{ matrix.goarch }} 25 | goversion: 1.21.1 26 | binary_name: "tianji-reporter" 27 | compress_assets: "OFF" 28 | asset_name: "tianji-reporter-${{ matrix.goos }}-${{ matrix.goarch }}" 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | src/client/public/tracker.js 3 | geo 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | pnpm-debug.log* 12 | lerna-debug.log* 13 | 14 | node_modules 15 | dist 16 | dist-ssr 17 | *.local 18 | 19 | # Editor directories and files 20 | .vscode/* 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-manager-strict=true 2 | package-manager-strict-version=false 3 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | src/client/routeTree.gen.ts 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "es5", 8 | "bracketSpacing": true, 9 | "arrowParens": "always", 10 | "jsxBracketSameLine": false, 11 | "plugins": ["prettier-plugin-tailwindcss"] 12 | } 13 | -------------------------------------------------------------------------------- /apps/appstore-review/.gitignore: -------------------------------------------------------------------------------- 1 | published_reviews.json 2 | publisher.json 3 | config.json 4 | lib 5 | -------------------------------------------------------------------------------- /apps/appstore-review/README.md: -------------------------------------------------------------------------------- 1 | ## appstore review to tianji survey 2 | 3 | Quick start and fork from `https://github.com/TradeMe/ReviewMe` 4 | -------------------------------------------------------------------------------- /apps/appstore-review/bin/appreview.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var reviewer = require('../lib/index'); 5 | var program = require('commander'); 6 | 7 | var configFile; 8 | var version = JSON.parse(fs.readFileSync(path.resolve(__dirname, './../package.json'), 'utf8')).version; 9 | 10 | program 11 | .version(version, '-v, --version') 12 | .arguments('') 13 | .action(function (file) { 14 | configFile = file; 15 | }) 16 | .parse(process.argv); 17 | 18 | if (typeof configFile === 'undefined') { 19 | console.error('No config file specified'); 20 | process.exit(1); 21 | } 22 | 23 | console.log('Loading config:', configFile) 24 | var config = JSON.parse(fs.readFileSync(configFile)); 25 | reviewer.start(config); 26 | -------------------------------------------------------------------------------- /apps/appstore-review/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tianji-appstore-review", 3 | "version": "1.0.4", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "appreview": "bin/appreview.js" 8 | }, 9 | "files": [ 10 | "bin", 11 | "lib" 12 | ], 13 | "scripts": { 14 | "dev": "tsx ./src/dev.ts", 15 | "build": "tsc", 16 | "prepare": "tsc", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "keywords": [], 20 | "author": "moonrailgun ", 21 | "license": "MIT", 22 | "dependencies": { 23 | "app-store-scraper": "^0.18.0", 24 | "axios": "1.7.7", 25 | "commander": "^4.1.1", 26 | "google-play-scraper": "^9.1.1", 27 | "googleapis": "^144.0.0", 28 | "tianji-client-sdk": "workspace:*" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/appstore-review/src/const.ts: -------------------------------------------------------------------------------- 1 | export const REVIEWS_LIMIT = 1000; 2 | export const DEFAULT_INTERVAL_SECONDS = 300; 3 | 4 | export const REVIEWS_STORES = { 5 | APP_STORE: 'app-store', 6 | GOOGLE_PLAY: 'google-play', 7 | }; 8 | -------------------------------------------------------------------------------- /apps/appstore-review/src/dev.ts: -------------------------------------------------------------------------------- 1 | import { start } from './'; 2 | import fs from 'fs'; 3 | 4 | var config = JSON.parse(String(fs.readFileSync('./config.json'))); 5 | start(config); 6 | -------------------------------------------------------------------------------- /apps/appstore-review/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as reviews from './reviews'; 2 | import { Config } from './types'; 3 | 4 | export function start(config: Config) { 5 | for (let i = 0; i < config.apps.length; i++) { 6 | const app = config.apps[i]; 7 | 8 | reviews.start({ 9 | verbose: config.verbose, 10 | dryRun: config.dryRun, 11 | interval: config.interval, 12 | tianji: config.tianji, 13 | 14 | // @ts-ignore 15 | publisherKey: app.publisherKey, 16 | appId: app.appId, 17 | store: app.store, 18 | // @ts-ignore 19 | regions: app.regions, 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/appstore-review/src/tianji.ts: -------------------------------------------------------------------------------- 1 | import { AppInformation, AppstoreConfig, GooglePlayConfig } from './types'; 2 | import { submitSurvey, initOpenapiSDK } from 'tianji-client-sdk'; 3 | 4 | export async function postToTianji( 5 | review: any, 6 | config: GooglePlayConfig | AppstoreConfig, 7 | appInformation: AppInformation 8 | ) { 9 | initOpenapiSDK(config.tianji.baseUrl); 10 | 11 | const payload = { 12 | ...review, 13 | ...appInformation, 14 | }; 15 | 16 | console.log('Report to tianji:', payload); 17 | await submitSurvey( 18 | config.tianji.workspaceId, 19 | config.tianji.surveyId, 20 | payload 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /apps/appstore-review/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "./lib", 5 | "noEmit": false 6 | }, 7 | "include": [ 8 | "./src/**/*" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = tab 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.yml] 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 140, 3 | "singleQuote": true, 4 | "semi": true, 5 | "useTabs": true 6 | } 7 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "daily-ai-trigger", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "deploy": "wrangler deploy", 7 | "dev": "wrangler dev", 8 | "dev:scheduled": "wrangler dev --test-scheduled", 9 | "start": "wrangler dev", 10 | "test": "vitest", 11 | "cf-typegen": "wrangler types" 12 | }, 13 | "devDependencies": { 14 | "@cloudflare/vitest-pool-workers": "^0.7.5", 15 | "@cloudflare/workers-types": "^4.20250310.0", 16 | "typescript": "^5.5.2", 17 | "vitest": "~3.0.8", 18 | "wrangler": "^3.112.0" 19 | }, 20 | "dependencies": { 21 | "dayjs": "^1.11.9", 22 | "tianji-client-sdk": "workspace:*" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["@cloudflare/workers-types/experimental", "@cloudflare/vitest-pool-workers"] 5 | }, 6 | "include": ["./**/*.ts", "../worker-configuration.d.ts"], 7 | "exclude": [] 8 | } 9 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/vitest.config.mts: -------------------------------------------------------------------------------- 1 | import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'; 2 | 3 | export default defineWorkersConfig({ 4 | test: { 5 | poolOptions: { 6 | workers: { 7 | wrangler: { configPath: './wrangler.jsonc' }, 8 | }, 9 | }, 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /apps/daily-ai-trigger/worker-configuration.d.ts: -------------------------------------------------------------------------------- 1 | // Generated by Wrangler 2 | // After adding bindings to `wrangler.jsonc`, regenerate this interface via `npm run cf-typegen` 3 | interface Env { 4 | BASE_URL: string; 5 | API_KEY: string; 6 | WORKSPACE_ID: string; 7 | SURVEY_ID: string; 8 | PAYLOAD_CONTENT_FIELD: string; 9 | LANGUAGE: string; 10 | FEISHU_WEBHOOK?: string; 11 | FEISHU_APP_ID?: string; 12 | FEISHU_APP_SECRET?: string; 13 | FEISHU_TABLE_APPTOKEN?: string; 14 | FEISHU_TABLE_ID?: string; 15 | } 16 | -------------------------------------------------------------------------------- /apps/mcp-server/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Debug MCP Server", 8 | "skipFiles": ["/**"], 9 | "outFiles": ["${workspaceFolder}/dist/**/*.js"], 10 | "runtimeExecutable": "npx", 11 | "runtimeArgs": [ 12 | "-y", 13 | "@modelcontextprotocol/inspector", 14 | "node", 15 | "dist/index.js" 16 | ], 17 | "console": "integratedTerminal", 18 | "preLaunchTask": "npm: watch", 19 | "serverReadyAction": { 20 | "action": "openExternally", 21 | "pattern": "running at (https?://\\S+)", 22 | "uriFormat": "%s?timeout=60000" 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /apps/mcp-server/.vscode/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "servers": { 3 | "tianji-mcp-server": { 4 | "type": "stdio", 5 | "command": "node", 6 | "args": ["${workspaceFolder}/dist/index.js"], 7 | "env": { 8 | "TIANJI_BASE_URL": "https://tianji.example.com", 9 | "TIANJI_API_KEY": "", 10 | "TIANJI_WORKSPACE_ID": "" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/mcp-server/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "watch", 7 | "problemMatcher": ["$tsc-watch"], 8 | "isBackground": true, 9 | "label": "npm: watch", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/mcp-server/src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 4 | 5 | import { createServer } from './server.js'; 6 | 7 | async function main() { 8 | const server: McpServer = createServer(); 9 | const transport = new StdioServerTransport(); 10 | await server.connect(transport); 11 | // console.debug("Tianji MCP Server running on stdio"); 12 | } 13 | 14 | main().catch((error) => { 15 | console.error('Fatal error in main():', error); 16 | process.exit(1); 17 | }); 18 | -------------------------------------------------------------------------------- /apps/mcp-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "sourceMap": true 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules"] 16 | } 17 | -------------------------------------------------------------------------------- /docker/clickhouse/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | 3 | services: 4 | clickhouse: 5 | image: clickhouse/clickhouse-server:latest 6 | container_name: tianji-clickhouse 7 | ports: 8 | - "127.0.0.1:8123:8123" # HTTP endpoint 9 | # - "9000:9000" # Native endpoint 10 | - "127.0.0.1:9004:9004" # Mysql endpoint 11 | environment: 12 | - CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT=1 13 | - CLICKHOUSE_DB=tianji 14 | - CLICKHOUSE_USER=tianji 15 | # - CLICKHOUSE_PASSWORD=tianji 16 | # volumes: 17 | # - clickhouse_data:/var/lib/clickhouse 18 | # - ./config.xml:/etc/clickhouse-server/config.xml 19 | restart: unless-stopped 20 | 21 | # volumes: 22 | # clickhouse_data: 23 | # driver: local 24 | # clickhouse_config: 25 | # driver: local 26 | -------------------------------------------------------------------------------- /docker/k8s/helm/.gitignore: -------------------------------------------------------------------------------- 1 | charts 2 | -------------------------------------------------------------------------------- /docker/k8s/helm/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /docker/k8s/helm/Chart.lock: -------------------------------------------------------------------------------- 1 | dependencies: 2 | - name: postgresql 3 | repository: https://charts.bitnami.com/bitnami 4 | version: 15.4.2 5 | digest: sha256:57308af2821726255fe0c52374260c2ea98f9091c03d82cf82b9c6d499e6f214 6 | generated: "2024-06-09T14:04:36.881437+08:00" 7 | -------------------------------------------------------------------------------- /docker/k8s/helm/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: {{ include "tianji.fullname" . }} 5 | labels: 6 | {{- include "tianji.labels" . | nindent 4 }} 7 | data: 8 | DATABASE_URL: postgresql://{{ .Values.postgresql.auth.username }}:{{ .Values.postgresql.auth.password }}@{{ .Release.Name }}-postgresql:5432/{{ .Values.postgresql.auth.database }} 9 | {{/* 10 | Rather than maintain a comprehensive ConfigMap, we map all sub-keys of the "env" value here. 11 | This allows for more flexibility and less Chart churn as Drone evolves. 12 | */}} 13 | {{- range $envKey, $envVal := .Values.env }} 14 | {{ $envKey | upper }}: {{ $envVal | quote }} 15 | {{- end }} 16 | -------------------------------------------------------------------------------- /docker/k8s/helm/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "tianji.fullname" . }} 5 | labels: 6 | {{- include "tianji.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "tianji.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /docker/k8s/helm/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "tianji.serviceAccountName" . }} 6 | labels: 7 | {{- include "tianji.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | automountServiceAccountToken: {{ .Values.serviceAccount.automount }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /docker/k8s/helm/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "tianji.fullname" . }}-test-connection" 5 | labels: 6 | {{- include "tianji.labels" . | nindent 4 }} 7 | annotations: 8 | "helm.sh/hook": test 9 | spec: 10 | containers: 11 | - name: wget 12 | image: busybox 13 | command: ['wget'] 14 | args: ['{{ include "tianji.fullname" . }}:{{ .Values.service.port }}'] 15 | restartPolicy: Never 16 | -------------------------------------------------------------------------------- /example/expo/.gitignore: -------------------------------------------------------------------------------- 1 | # Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files 2 | 3 | # dependencies 4 | node_modules/ 5 | 6 | # Expo 7 | .expo/ 8 | dist/ 9 | web-build/ 10 | expo-env.d.ts 11 | 12 | # Native 13 | *.orig.* 14 | *.jks 15 | *.p8 16 | *.p12 17 | *.key 18 | *.mobileprovision 19 | 20 | # Metro 21 | .metro-health-check* 22 | 23 | # debug 24 | npm-debug.* 25 | yarn-debug.* 26 | yarn-error.* 27 | 28 | # macOS 29 | .DS_Store 30 | *.pem 31 | 32 | # local env files 33 | .env*.local 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | 38 | app-example 39 | -------------------------------------------------------------------------------- /example/expo/.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /example/expo/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /example/expo/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /example/expo/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/favicon.png -------------------------------------------------------------------------------- /example/expo/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/icon.png -------------------------------------------------------------------------------- /example/expo/assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /example/expo/assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/react-logo.png -------------------------------------------------------------------------------- /example/expo/assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /example/expo/assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /example/expo/assets/images/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/expo/assets/images/splash-icon.png -------------------------------------------------------------------------------- /example/expo/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: [ 5 | ["babel-preset-expo", { jsxImportSource: "nativewind" }], 6 | "nativewind/babel", 7 | ], 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /example/expo/components/ExternalLink.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'expo-router'; 2 | import { openBrowserAsync } from 'expo-web-browser'; 3 | import { type ComponentProps } from 'react'; 4 | import { Platform } from 'react-native'; 5 | 6 | type Props = Omit, 'href'> & { href: string }; 7 | 8 | export function ExternalLink({ href, ...rest }: Props) { 9 | return ( 10 | { 15 | if (Platform.OS !== 'web') { 16 | // Prevent the default behavior of linking to the default browser on native. 17 | event.preventDefault(); 18 | // Open the link in an in-app browser. 19 | await openBrowserAsync(href); 20 | } 21 | }} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /example/expo/components/HapticTab.tsx: -------------------------------------------------------------------------------- 1 | import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; 2 | import { PlatformPressable } from '@react-navigation/elements'; 3 | import * as Haptics from 'expo-haptics'; 4 | import React from 'react'; 5 | 6 | export function HapticTab(props: BottomTabBarButtonProps) { 7 | return ( 8 | { 11 | if (process.env.EXPO_OS === 'ios') { 12 | // Add a soft haptic feedback when pressing down on the tabs. 13 | Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); 14 | } 15 | props.onPressIn?.(ev); 16 | }} 17 | /> 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /example/expo/components/__tests__/ThemedText-test.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import renderer from 'react-test-renderer'; 3 | 4 | import { ThemedText } from '../ThemedText'; 5 | 6 | it(`renders correctly`, () => { 7 | const tree = renderer.create(Snapshot test!).toJSON(); 8 | 9 | expect(tree).toMatchSnapshot(); 10 | }); 11 | -------------------------------------------------------------------------------- /example/expo/components/__tests__/__snapshots__/ThemedText-test.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders correctly 1`] = ` 4 | 22 | Snapshot test! 23 | 24 | `; 25 | -------------------------------------------------------------------------------- /example/expo/components/ui/IconSymbol.ios.tsx: -------------------------------------------------------------------------------- 1 | import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; 2 | import { StyleProp, ViewStyle } from 'react-native'; 3 | 4 | export function IconSymbol({ 5 | name, 6 | size = 24, 7 | color, 8 | style, 9 | weight = 'regular', 10 | }: { 11 | name: SymbolViewProps['name']; 12 | size?: number; 13 | color: string; 14 | style?: StyleProp; 15 | weight?: SymbolWeight; 16 | }) { 17 | return ( 18 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /example/expo/components/ui/TabBarBackground.ios.tsx: -------------------------------------------------------------------------------- 1 | import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; 2 | import { BlurView } from 'expo-blur'; 3 | import { StyleSheet } from 'react-native'; 4 | import { useSafeAreaInsets } from 'react-native-safe-area-context'; 5 | 6 | export default function BlurTabBarBackground() { 7 | return ( 8 | 15 | ); 16 | } 17 | 18 | export function useBottomTabOverflow() { 19 | const tabHeight = useBottomTabBarHeight(); 20 | const { bottom } = useSafeAreaInsets(); 21 | return tabHeight - bottom; 22 | } 23 | -------------------------------------------------------------------------------- /example/expo/components/ui/TabBarBackground.tsx: -------------------------------------------------------------------------------- 1 | // This is a shim for web and Android where the tab bar is generally opaque. 2 | export default undefined; 3 | 4 | export function useBottomTabOverflow() { 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /example/expo/components/ui/gluestack-ui-provider/script.ts: -------------------------------------------------------------------------------- 1 | export const script = (mode: string) => { 2 | const documentElement = document.documentElement; 3 | 4 | function getSystemColorMode() { 5 | return window.matchMedia('(prefers-color-scheme: dark)').matches 6 | ? 'dark' 7 | : 'light'; 8 | } 9 | 10 | try { 11 | const isSystem = mode === 'system'; 12 | const theme = isSystem ? getSystemColorMode() : mode; 13 | documentElement.classList.remove(theme === 'light' ? 'dark' : 'light'); 14 | documentElement.classList.add(theme); 15 | documentElement.style.colorScheme = theme; 16 | } catch (e) { 17 | console.error(e); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /example/expo/components/ui/gluestack-ui-provider/types.ts: -------------------------------------------------------------------------------- 1 | export type ModeType = 'light' | 'dark' | 'system'; 2 | -------------------------------------------------------------------------------- /example/expo/components/ui/hstack/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { VariantProps } from '@gluestack-ui/nativewind-utils'; 3 | import { View } from 'react-native'; 4 | import type { ViewProps } from 'react-native'; 5 | import { hstackStyle } from './styles'; 6 | 7 | type IHStackProps = ViewProps & VariantProps; 8 | 9 | const HStack = React.forwardRef, IHStackProps>( 10 | function HStack({ className, space, reversed, ...props }, ref) { 11 | return ( 12 | 17 | ); 18 | } 19 | ); 20 | 21 | HStack.displayName = 'HStack'; 22 | 23 | export { HStack }; 24 | -------------------------------------------------------------------------------- /example/expo/components/ui/hstack/index.web.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { VariantProps } from '@gluestack-ui/nativewind-utils'; 3 | import { hstackStyle } from './styles'; 4 | 5 | type IHStackProps = React.ComponentPropsWithoutRef<'div'> & 6 | VariantProps; 7 | 8 | const HStack = React.forwardRef, IHStackProps>( 9 | function HStack({ className, space, reversed, ...props }, ref) { 10 | return ( 11 |
16 | ); 17 | } 18 | ); 19 | 20 | HStack.displayName = 'HStack'; 21 | 22 | export { HStack }; 23 | -------------------------------------------------------------------------------- /example/expo/components/ui/hstack/styles.tsx: -------------------------------------------------------------------------------- 1 | import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb'; 2 | import { tva } from '@gluestack-ui/nativewind-utils/tva'; 3 | 4 | const baseStyle = isWeb 5 | ? 'flex relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none' 6 | : ''; 7 | 8 | export const hstackStyle = tva({ 9 | base: `flex-row ${baseStyle}`, 10 | variants: { 11 | space: { 12 | 'xs': 'gap-1', 13 | 'sm': 'gap-2', 14 | 'md': 'gap-3', 15 | 'lg': 'gap-4', 16 | 'xl': 'gap-5', 17 | '2xl': 'gap-6', 18 | '3xl': 'gap-7', 19 | '4xl': 'gap-8', 20 | }, 21 | reversed: { 22 | true: 'flex-row-reverse', 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /example/expo/components/ui/vstack/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { VariantProps } from '@gluestack-ui/nativewind-utils'; 3 | import { View } from 'react-native'; 4 | 5 | import { vstackStyle } from './styles'; 6 | 7 | type IVStackProps = React.ComponentProps & 8 | VariantProps; 9 | 10 | const VStack = React.forwardRef, IVStackProps>( 11 | function VStack({ className, space, reversed, ...props }, ref) { 12 | return ( 13 | 18 | ); 19 | } 20 | ); 21 | 22 | VStack.displayName = 'VStack'; 23 | 24 | export { VStack }; 25 | -------------------------------------------------------------------------------- /example/expo/components/ui/vstack/index.web.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import type { VariantProps } from '@gluestack-ui/nativewind-utils'; 3 | 4 | import { vstackStyle } from './styles'; 5 | 6 | type IVStackProps = React.ComponentProps<'div'> & 7 | VariantProps; 8 | 9 | const VStack = React.forwardRef, IVStackProps>( 10 | function VStack({ className, space, reversed, ...props }, ref) { 11 | return ( 12 |
17 | ); 18 | } 19 | ); 20 | 21 | VStack.displayName = 'VStack'; 22 | 23 | export { VStack }; 24 | -------------------------------------------------------------------------------- /example/expo/components/ui/vstack/styles.tsx: -------------------------------------------------------------------------------- 1 | import { isWeb } from '@gluestack-ui/nativewind-utils/IsWeb'; 2 | import { tva } from '@gluestack-ui/nativewind-utils/tva'; 3 | 4 | const baseStyle = isWeb 5 | ? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none' 6 | : ''; 7 | 8 | export const vstackStyle = tva({ 9 | base: `flex-col ${baseStyle}`, 10 | variants: { 11 | space: { 12 | 'xs': 'gap-1', 13 | 'sm': 'gap-2', 14 | 'md': 'gap-3', 15 | 'lg': 'gap-4', 16 | 'xl': 'gap-5', 17 | '2xl': 'gap-6', 18 | '3xl': 'gap-7', 19 | '4xl': 'gap-8', 20 | }, 21 | reversed: { 22 | true: 'flex-col-reverse', 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /example/expo/global.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /example/expo/gluestack-ui.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tailwind": { 3 | "config": "tailwind.config.js", 4 | "css": "global.css" 5 | }, 6 | "app": { 7 | "entry": "app/_layout.tsx", 8 | "components": "components/ui" 9 | } 10 | } -------------------------------------------------------------------------------- /example/expo/hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | export { useColorScheme } from 'react-native'; 2 | -------------------------------------------------------------------------------- /example/expo/hooks/useColorScheme.web.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useColorScheme as useRNColorScheme } from 'react-native'; 3 | 4 | /** 5 | * To support static rendering, this value needs to be re-calculated on the client side for web 6 | */ 7 | export function useColorScheme() { 8 | const [hasHydrated, setHasHydrated] = useState(false); 9 | 10 | useEffect(() => { 11 | setHasHydrated(true); 12 | }, []); 13 | 14 | const colorScheme = useRNColorScheme(); 15 | 16 | if (hasHydrated) { 17 | return colorScheme; 18 | } 19 | 20 | return 'light'; 21 | } 22 | -------------------------------------------------------------------------------- /example/expo/hooks/useThemeColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about light and dark modes: 3 | * https://docs.expo.dev/guides/color-schemes/ 4 | */ 5 | 6 | import { Colors } from '@/constants/Colors'; 7 | import { useColorScheme } from '@/hooks/useColorScheme'; 8 | 9 | export function useThemeColor( 10 | props: { light?: string; dark?: string }, 11 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 12 | ) { 13 | const theme = useColorScheme() ?? 'light'; 14 | const colorFromProps = props[theme]; 15 | 16 | if (colorFromProps) { 17 | return colorFromProps; 18 | } else { 19 | return Colors[theme][colorName]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/expo/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require("expo/metro-config"); 2 | const { withNativeWind } = require('nativewind/metro'); 3 | 4 | const config = getDefaultConfig(__dirname) 5 | 6 | module.exports = withNativeWind(config, { input: './global.css' }) 7 | -------------------------------------------------------------------------------- /example/expo/nativewind-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /example/expo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": [ 7 | "./*" 8 | ], 9 | "tailwind.config": [ 10 | "./tailwind.config.js" 11 | ] 12 | } 13 | }, 14 | "include": [ 15 | "**/*.ts", 16 | "**/*.tsx", 17 | ".expo/types/**/*.ts", 18 | "expo-env.d.ts", 19 | "nativewind-env.d.ts" 20 | ] 21 | } -------------------------------------------------------------------------------- /example/web/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /example/web/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /example/web/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /example/web/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /example/web/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/web/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/web/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /example/web/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msgbyte/tianji/12699b0bcf40f78db2f123a693d7886b2cb4c7c5/example/web/src/app/favicon.ico -------------------------------------------------------------------------------- /example/web/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | --background: #ffffff; 7 | --foreground: #171717; 8 | } 9 | 10 | /* @media (prefers-color-scheme: dark) { 11 | :root { 12 | --background: #0a0a0a; 13 | --foreground: #ededed; 14 | } 15 | } */ 16 | 17 | body { 18 | color: var(--foreground); 19 | background: var(--background); 20 | font-family: Arial, Helvetica, sans-serif; 21 | } 22 | -------------------------------------------------------------------------------- /example/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | export default { 4 | content: [ 5 | "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", 6 | "./src/components/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./src/app/**/*.{js,ts,jsx,tsx,mdx}", 8 | ], 9 | theme: { 10 | extend: { 11 | colors: { 12 | background: "var(--background)", 13 | foreground: "var(--foreground)", 14 | }, 15 | }, 16 | }, 17 | plugins: [], 18 | } satisfies Config; 19 | -------------------------------------------------------------------------------- /example/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./src/*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /packages/client-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /packages/client-sdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tianji-client-sdk", 3 | "version": "1.3.1", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "prepare": "tsc", 9 | "generate:client": "openapi-ts -i ../../website/openapi.json -o src/open/client", 10 | "test": "vitest" 11 | }, 12 | "files": [ 13 | "lib", 14 | "src" 15 | ], 16 | "keywords": [ 17 | "tianji" 18 | ], 19 | "author": "moonrailgun ", 20 | "license": "MIT", 21 | "dependencies": { 22 | "load-script": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "@hey-api/openapi-ts": "^0.42.1", 26 | "@testing-library/dom": "^10.0.0", 27 | "happy-dom": "15.11.6", 28 | "msw": "^2.2.13", 29 | "typescript": "^5.4.5", 30 | "vitest": "~3.0.8" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/client-sdk/src/config.ts: -------------------------------------------------------------------------------- 1 | import { OpenAPI } from './open/client'; 2 | 3 | interface OpenapiSDKOptions { 4 | header?: Record; 5 | apiKey?: string; 6 | } 7 | export function initOpenapiSDK(baseUrl: string, options?: OpenapiSDKOptions) { 8 | OpenAPI.BASE = baseUrl.endsWith('/open') ? baseUrl : `${baseUrl}/open`; 9 | 10 | if (options?.apiKey) { 11 | OpenAPI.TOKEN = options.apiKey; 12 | } 13 | 14 | if (options?.header) { 15 | OpenAPI.HEADERS = options.header; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/client-sdk/src/feed.ts: -------------------------------------------------------------------------------- 1 | import { $OpenApiTs, FeedService } from './open/client'; 2 | 3 | export async function sendFeed( 4 | channelId: string, 5 | payload: $OpenApiTs['/feed/{channelId}/send']['post']['req']['requestBody'] 6 | ) { 7 | const res = await FeedService.feedSendEvent({ 8 | channelId, 9 | requestBody: payload, 10 | }); 11 | 12 | return res; 13 | } 14 | -------------------------------------------------------------------------------- /packages/client-sdk/src/index.ts: -------------------------------------------------------------------------------- 1 | export { initOpenapiSDK } from './config'; 2 | export { openApiClient } from './open'; 3 | export * from './tracker'; 4 | export * from './survey'; 5 | export * from './feed'; 6 | export * from './application'; 7 | -------------------------------------------------------------------------------- /packages/client-sdk/src/open/client/core/ApiError.ts: -------------------------------------------------------------------------------- 1 | import type { ApiRequestOptions } from './ApiRequestOptions'; 2 | import type { ApiResult } from './ApiResult'; 3 | 4 | export class ApiError extends Error { 5 | public readonly url: string; 6 | public readonly status: number; 7 | public readonly statusText: string; 8 | public readonly body: unknown; 9 | public readonly request: ApiRequestOptions; 10 | 11 | constructor(request: ApiRequestOptions, response: ApiResult, message: string) { 12 | super(message); 13 | 14 | this.name = 'ApiError'; 15 | this.url = response.url; 16 | this.status = response.status; 17 | this.statusText = response.statusText; 18 | this.body = response.body; 19 | this.request = request; 20 | } 21 | } -------------------------------------------------------------------------------- /packages/client-sdk/src/open/client/core/ApiRequestOptions.ts: -------------------------------------------------------------------------------- 1 | export type ApiRequestOptions = { 2 | readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; 3 | readonly url: string; 4 | readonly path?: Record; 5 | readonly cookies?: Record; 6 | readonly headers?: Record; 7 | readonly query?: Record; 8 | readonly formData?: Record; 9 | readonly body?: any; 10 | readonly mediaType?: string; 11 | readonly responseHeader?: string; 12 | readonly errors?: Record; 13 | }; -------------------------------------------------------------------------------- /packages/client-sdk/src/open/client/core/ApiResult.ts: -------------------------------------------------------------------------------- 1 | export type ApiResult = { 2 | readonly body: TData; 3 | readonly ok: boolean; 4 | readonly status: number; 5 | readonly statusText: string; 6 | readonly url: string; 7 | }; -------------------------------------------------------------------------------- /packages/client-sdk/src/open/client/index.ts: -------------------------------------------------------------------------------- 1 | // This file is auto-generated by @hey-api/openapi-ts 2 | export { ApiError } from './core/ApiError'; 3 | export { CancelablePromise, CancelError } from './core/CancelablePromise'; 4 | export { OpenAPI, type OpenAPIConfig } from './core/OpenAPI'; 5 | export * from './schemas.gen'; 6 | export * from './services.gen'; 7 | export * from './types.gen'; -------------------------------------------------------------------------------- /packages/client-sdk/src/open/index.ts: -------------------------------------------------------------------------------- 1 | export * as openApiClient from './client'; 2 | -------------------------------------------------------------------------------- /packages/client-sdk/src/survey.ts: -------------------------------------------------------------------------------- 1 | import { SurveyService } from './open/client'; 2 | 3 | export async function getSurveyInfo(workspaceId: string, surveyId: string) { 4 | const survey = await SurveyService.surveyGet({ 5 | workspaceId, 6 | surveyId, 7 | }); 8 | 9 | return survey; 10 | } 11 | 12 | export async function submitSurvey( 13 | workspaceId: string, 14 | surveyId: string, 15 | payload: Record 16 | ) { 17 | const res = await SurveyService.surveySubmit({ 18 | workspaceId, 19 | surveyId, 20 | requestBody: { 21 | payload, 22 | }, 23 | }); 24 | 25 | return res; 26 | } 27 | -------------------------------------------------------------------------------- /packages/client-sdk/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface IdentifyPayload { 2 | id?: string; 3 | email?: string; 4 | username?: string; 5 | avatar?: string; 6 | [key: string]: any; 7 | } 8 | -------------------------------------------------------------------------------- /packages/client-sdk/tests/setup.ts: -------------------------------------------------------------------------------- 1 | import { afterAll, afterEach, beforeAll } from 'vitest'; 2 | import { setupServer } from 'msw/node'; 3 | import { HttpResponse, http } from 'msw'; 4 | 5 | export const restHandlers = [ 6 | http.get('https://example.com/*', () => { 7 | return HttpResponse.text('var a = 1;'); 8 | }), 9 | ]; 10 | 11 | const server = setupServer(...restHandlers); 12 | 13 | beforeAll(() => server.listen({ onUnhandledRequest: 'error' })); 14 | 15 | afterAll(() => server.close()); 16 | 17 | afterEach(() => server.resetHandlers()); 18 | -------------------------------------------------------------------------------- /packages/client-sdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "CommonJS", 5 | "outDir": "./lib", 6 | "baseUrl": ".", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "noEmit": false, 10 | }, 11 | "include": ["./src/**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/client-sdk/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | environment: 'happy-dom', 6 | setupFiles: ['./tests/setup.ts'], 7 | }, 8 | }); 9 | -------------------------------------------------------------------------------- /packages/react-native/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /packages/react-native/src/index.ts: -------------------------------------------------------------------------------- 1 | import packageJson from '../package.json'; 2 | import { 3 | initApplication as _initApplication, 4 | ApplicationTrackingOptions, 5 | identifyApplicationUser, 6 | } from 'tianji-client-sdk'; 7 | import { Platform } from 'react-native'; 8 | import { getVersion } from 'react-native-device-info'; 9 | 10 | export * from 'tianji-client-sdk'; 11 | 12 | export function initApplication(_options: ApplicationTrackingOptions) { 13 | _initApplication(_options); 14 | 15 | identifyApplicationUser({ 16 | os: Platform.OS, 17 | version: getVersion(), 18 | sdkVersion: packageJson.version, 19 | }); 20 | } 21 | -------------------------------------------------------------------------------- /packages/react-native/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "outDir": "./lib", 6 | "baseUrl": ".", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "noEmit": false, 10 | "rootDir": "./src" 11 | }, 12 | "include": ["./src/**/*"] 13 | } 14 | -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | lib 2 | -------------------------------------------------------------------------------- /packages/react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tianji-client-react", 3 | "version": "1.0.2", 4 | "description": "", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "build": "tsc", 8 | "prepare": "tsc", 9 | "generate:client": "openapi-ts -i ../../website/openapi.json -o src/open/client", 10 | "test": "vitest" 11 | }, 12 | "keywords": [ 13 | "tianji" 14 | ], 15 | "author": "moonrailgun ", 16 | "license": "MIT", 17 | "dependencies": { 18 | "tianji-client-sdk": "workspace:^" 19 | }, 20 | "devDependencies": { 21 | "@types/react": "^18.2.22", 22 | "react": "^18.3.1" 23 | }, 24 | "peerDependencies": { 25 | "@types/react": "^18.2.22", 26 | "react": "^18.3.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/react/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useTianjiSurvey'; 2 | export * from 'tianji-client-sdk'; 3 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "outDir": "./lib", 6 | "baseUrl": ".", 7 | "declaration": true, 8 | "declarationMap": true, 9 | "noEmit": false, 10 | }, 11 | "include": ["./src/**/*"] 12 | } 13 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'src/client' 3 | - 'src/server' 4 | - 'src/shared' 5 | - 'website' 6 | - 'packages/*' 7 | - 'apps/*' 8 | - 'example/web' 9 | -------------------------------------------------------------------------------- /reporter/utils/utils_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestGetPayload(t *testing.T) { 10 | payload := GetReportDataPaylod(5, false) 11 | 12 | fmt.Println("{}", payload) 13 | } 14 | 15 | func TestGetDockerStat(t *testing.T) { 16 | dockerPayloads, err := GetDockerStat() 17 | assert.NoError(t, err, "Should can get docker stat") 18 | 19 | fmt.Println("{}", dockerPayloads) 20 | } 21 | -------------------------------------------------------------------------------- /scripts/build-openapi-schema.ts: -------------------------------------------------------------------------------- 1 | import { trpcOpenapiDocument } from '../src/server/trpc'; 2 | import fs from 'fs-extra'; 3 | import path from 'path'; 4 | import { fileURLToPath } from 'url'; 5 | 6 | const __filename = fileURLToPath(import.meta.url); 7 | const __dirname = path.dirname(__filename); 8 | 9 | const target = path.resolve(__dirname, '../website/openapi.json'); 10 | fs.writeJSON(target, trpcOpenapiDocument) 11 | .then(() => { 12 | console.log('openapi schema has been write into:', target); 13 | }) 14 | .catch((err) => { 15 | console.error(err); 16 | }); 17 | -------------------------------------------------------------------------------- /scripts/build-tracker.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import * as vite from 'vite'; 3 | 4 | console.log('Start Build Tracker'); 5 | 6 | vite 7 | .build({ 8 | build: { 9 | lib: { 10 | entry: resolve(process.cwd(), './src/tracker/index.js'), 11 | name: 'tianji', 12 | fileName: () => 'tracker.js', 13 | formats: ['iife'], 14 | }, 15 | emptyOutDir: false, 16 | outDir: resolve(process.cwd(), './src/client/public'), 17 | }, 18 | }) 19 | .then((res) => { 20 | console.log('Build Tracker Completed'); 21 | }); 22 | -------------------------------------------------------------------------------- /src/client/api/cache.ts: -------------------------------------------------------------------------------- 1 | import { QueryClient } from '@tanstack/react-query'; 2 | 3 | export const queryClient = new QueryClient({ 4 | defaultOptions: { 5 | queries: { 6 | retry: false, 7 | refetchOnWindowFocus: false, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /src/client/api/model/application.ts: -------------------------------------------------------------------------------- 1 | import { queryClient } from '../cache'; 2 | import { request } from '../request'; 3 | import { AppRouterOutput } from '../trpc'; 4 | 5 | export type ApplicationInfo = NonNullable< 6 | AppRouterOutput['application']['info'] 7 | >; 8 | 9 | export function refreshWorkspaceApplications(workspaceId: string) { 10 | queryClient.refetchQueries({ 11 | queryKey: ['applications', workspaceId], 12 | }); 13 | } 14 | -------------------------------------------------------------------------------- /src/client/api/model/monitor.ts: -------------------------------------------------------------------------------- 1 | import { defaultErrorHandler, defaultSuccessHandler, trpc } from '../trpc'; 2 | 3 | export function useMonitorUpsert() { 4 | const utils = trpc.useUtils(); 5 | const mutation = trpc.monitor.upsert.useMutation({ 6 | onSuccess: (data) => { 7 | utils.monitor.all.refetch({ 8 | workspaceId: data.workspaceId, 9 | }); 10 | 11 | utils.monitor.get.refetch({ 12 | workspaceId: data.workspaceId, 13 | monitorId: data.id, 14 | }); 15 | 16 | defaultSuccessHandler(); 17 | }, 18 | onError: defaultErrorHandler, 19 | }); 20 | 21 | return mutation; 22 | } 23 | -------------------------------------------------------------------------------- /src/client/api/model/user.ts: -------------------------------------------------------------------------------- 1 | import dayjs from 'dayjs'; 2 | 3 | /** 4 | * Mock 5 | * return local, or fetch remote data 6 | */ 7 | export function getUserTimezone(): string { 8 | return dayjs.tz.guess() ?? 'utc'; 9 | } 10 | -------------------------------------------------------------------------------- /src/client/api/model/website.ts: -------------------------------------------------------------------------------- 1 | import { queryClient } from '../cache'; 2 | import { request } from '../request'; 3 | import { AppRouterOutput } from '../trpc'; 4 | 5 | export type WebsiteInfo = NonNullable; 6 | 7 | export function refreshWorkspaceWebsites(workspaceId: string) { 8 | queryClient.refetchQueries({ 9 | queryKey: ['websites', workspaceId], 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/client/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "./index.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/utils/style" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/client/components/AutoLoadingButton.tsx: -------------------------------------------------------------------------------- 1 | import { Button, ButtonProps } from 'antd'; 2 | import React, { useState } from 'react'; 3 | import { useEvent } from '../hooks/useEvent'; 4 | 5 | export const AutoLoadingButton: React.FC = React.memo((props) => { 6 | const [loading, setLoading] = useState(false); 7 | 8 | const handleClick = useEvent( 9 | async (e: React.MouseEvent) => { 10 | setLoading(true); 11 | await props.onClick?.(e); 12 | setLoading(false); 13 | } 14 | ); 15 | return