Compare commits

..

1 Commits

Author SHA1 Message Date
moonrailgun
17bad8e1b4 feat: wip: add k8s server status report 2024-10-20 14:01:42 +08:00
120 changed files with 673 additions and 5636 deletions

View File

@ -1,82 +1,5 @@
## [1.16.5](https://github.com/msgbyte/tianji/compare/v1.16.4...v1.16.5) (2024-11-02)
### Features
* add webhookSignature in feed channel ([6b3631e](https://github.com/msgbyte/tianji/commit/6b3631eae186b9cacf64d0ddcfbb66378e041281))
### Bug Fixes
* add key to Fragment in map for monitor items ([9949b97](https://github.com/msgbyte/tianji/commit/9949b973bd63b4ad6b5e71f7b819442f505c09a6))
* retrieve date as string ([a8a47ed](https://github.com/msgbyte/tianji/commit/a8a47ed94dda87c3fe4cdecc0acb9a31f53f00a5))
### Others
* fix ci problem ([59b8746](https://github.com/msgbyte/tianji/commit/59b874644fd3427bc86cd2a7e948054e827de080))
* refactor status header and add typescript and translation support ([f637ade](https://github.com/msgbyte/tianji/commit/f637ade70f230fbf472bdee84105c9b284d6b8d4))
* update amount in stripe ([2725056](https://github.com/msgbyte/tianji/commit/272505669e450d882930cbf594dac39a879b2072))
* update webhooks signature api guide ([266b08f](https://github.com/msgbyte/tianji/commit/266b08f2da16d0457a5a44b4a7a251d28502abc9))
## [1.16.4](https://github.com/msgbyte/tianji/compare/v1.16.3...v1.16.4) (2024-10-27)
### Features
* add stripe feed integration ([09d0f02](https://github.com/msgbyte/tianji/commit/09d0f02d844159565e97bb64f076e0bbe218ce98))
### Others
* update currency symbols in feed ([98298c4](https://github.com/msgbyte/tianji/commit/98298c43670326b4e2300a6bbdeee3daa53f0eb3))
## [1.16.3](https://github.com/msgbyte/tianji/compare/v1.16.2...v1.16.3) (2024-10-24)
### Others
* fix ci problem and upgrade version ([1c5737e](https://github.com/msgbyte/tianji/commit/1c5737e588d19e0657be6437792cf4484b6fdddb))
## [1.16.2](https://github.com/msgbyte/tianji/compare/v1.16.1...v1.16.2) (2024-10-23)
### Features
* add prometheus report support ([fcb8f22](https://github.com/msgbyte/tianji/commit/fcb8f221168281ab710d3d3f12064a99d17b39e7))
### Bug Fixes
* fix a bug which will match incorrect path [#115](https://github.com/msgbyte/tianji/issues/115) ([79667a9](https://github.com/msgbyte/tianji/commit/79667a9644b78451400acb6a6bbf07b6ca61e6e0))
### Document
* update README ([1df32dc](https://github.com/msgbyte/tianji/commit/1df32dc2579f32649afd6c008512c1190a45fd9e))
### Others
* fix ci problem ([554f902](https://github.com/msgbyte/tianji/commit/554f9025847defe0b05492cf07a5dc8acc6c3685))
* update openapi document ([e402ee1](https://github.com/msgbyte/tianji/commit/e402ee1688bb77d83463ce70c5e730c97f68a695))
## [1.16.1](https://github.com/msgbyte/tianji/compare/v1.16.0...v1.16.1) (2024-10-20)
### Features
* add test notify ([4e3fd9d](https://github.com/msgbyte/tianji/commit/4e3fd9db64629f7721e6092b86b06144c47f521d))
* add timezone support [#114](https://github.com/msgbyte/tianji/issues/114) ([c7e20df](https://github.com/msgbyte/tianji/commit/c7e20df516bf3a991ce46c937223948bcdb6b8f0))
* add workspace settings manage ([3dca8fc](https://github.com/msgbyte/tianji/commit/3dca8fc27c82bd96dbab423b111e4de57f3b4bd8))
### Others
* update cronjob clear time ([83850f2](https://github.com/msgbyte/tianji/commit/83850f2981ded0b6624556ee3430f684752b8ea3))
## [1.16.0](https://github.com/msgbyte/tianji/compare/v1.15.8...v1.16.0) (2024-10-19)

View File

@ -37,8 +37,9 @@ It's good to specialize in one thing, if we are experts in related abilities we
- [x] waitlist
- [x] survey
- [ ] survey page
- [x] lighthouse report
- [ ] lighthouse report
- [x] hooks
- [ ] links
- [x] helm install support
- [x] allow install from public
- [ ] improve monitor reporter usage

View File

@ -1,7 +1,7 @@
{
"name": "tianji",
"private": true,
"version": "1.16.5",
"version": "1.16.0",
"type": "module",
"scripts": {
"dev": "concurrently --kill-others npm:dev:server npm:dev:web",

View File

@ -1,6 +1,6 @@
{
"name": "tianji-client-sdk",
"version": "1.1.1",
"version": "1.1.0",
"description": "",
"main": "lib/index.js",
"scripts": {

View File

@ -47,7 +47,7 @@ export const OpenAPI: OpenAPIConfig = {
PASSWORD: undefined,
TOKEN: undefined,
USERNAME: undefined,
VERSION: '1.16.1',
VERSION: '1.15.7',
WITH_CREDENTIALS: false,
interceptors: {
request: new Interceptors(),

View File

@ -120,10 +120,10 @@ export class WorkspaceService {
* @returns unknown Successful response
* @throws ApiError
*/
public static workspaceDelete(data: $OpenApiTs['/workspace//{workspaceId}/del']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace//{workspaceId}/del']['delete']['res'][200]> {
public static workspaceDelete(data: $OpenApiTs['/workspace//{workspaceId}']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace//{workspaceId}']['delete']['res'][200]> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/workspace//{workspaceId}/del',
url: '/workspace//{workspaceId}',
path: {
workspaceId: data.workspaceId
}
@ -146,25 +146,6 @@ export class WorkspaceService {
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
* @param data.requestBody
* @returns unknown Successful response
* @throws ApiError
*/
public static workspaceUpdateSettings(data: $OpenApiTs['/workspace//{workspaceId}/updateSettings']['post']['req']): CancelablePromise<$OpenApiTs['/workspace//{workspaceId}/updateSettings']['post']['res'][200]> {
return __request(OpenAPI, {
method: 'POST',
url: '/workspace//{workspaceId}/updateSettings',
path: {
workspaceId: data.workspaceId
},
body: data.requestBody,
mediaType: 'application/json'
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
@ -604,10 +585,28 @@ export class MonitorService {
* @returns unknown Successful response
* @throws ApiError
*/
public static monitorGet(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/get']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/get']['get']['res'][200]> {
public static monitorGet(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}']['get']['res'][200]> {
return __request(OpenAPI, {
method: 'GET',
url: '/workspace/{workspaceId}/monitor/{monitorId}/get',
url: '/workspace/{workspaceId}/monitor/{monitorId}',
path: {
workspaceId: data.workspaceId,
monitorId: data.monitorId
}
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
* @param data.monitorId
* @returns unknown Successful response
* @throws ApiError
*/
public static monitorDelete(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}']['delete']['res'][200]> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/workspace/{workspaceId}/monitor/{monitorId}',
path: {
workspaceId: data.workspaceId,
monitorId: data.monitorId
@ -649,24 +648,6 @@ export class MonitorService {
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
* @param data.monitorId
* @returns unknown Successful response
* @throws ApiError
*/
public static monitorDelete(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/del']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/del']['delete']['res'][200]> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/workspace/{workspaceId}/monitor/{monitorId}/del',
path: {
workspaceId: data.workspaceId,
monitorId: data.monitorId
}
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
@ -734,42 +715,6 @@ export class MonitorService {
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
* @param data.monitorId
* @returns unknown Successful response
* @throws ApiError
*/
public static monitorPublicSummary(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/publicSummary']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/publicSummary']['get']['res'][200]> {
return __request(OpenAPI, {
method: 'GET',
url: '/workspace/{workspaceId}/monitor/{monitorId}/publicSummary',
path: {
workspaceId: data.workspaceId,
monitorId: data.monitorId
}
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
* @param data.monitorId
* @returns unknown Successful response
* @throws ApiError
*/
public static monitorPublicData(data: $OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/publicData']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/monitor/{monitorId}/publicData']['get']['res'][200]> {
return __request(OpenAPI, {
method: 'GET',
url: '/workspace/{workspaceId}/monitor/{monitorId}/publicData',
path: {
workspaceId: data.workspaceId,
monitorId: data.monitorId
}
});
}
/**
* @param data The data for the request.
* @param data.workspaceId
@ -1213,10 +1158,10 @@ export class SurveyService {
* @returns unknown Successful response
* @throws ApiError
*/
public static surveyGet(data: $OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}/get']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}/get']['get']['res'][200]> {
public static surveyGet(data: $OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}']['get']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}']['get']['res'][200]> {
return __request(OpenAPI, {
method: 'GET',
url: '/workspace/{workspaceId}/survey/{surveyId}/get',
url: '/workspace/{workspaceId}/survey/{surveyId}',
path: {
workspaceId: data.workspaceId,
surveyId: data.surveyId
@ -1527,10 +1472,10 @@ export class FeedService {
* @returns unknown Successful response
* @throws ApiError
*/
public static feedDeleteChannel(data: $OpenApiTs['/workspace/{workspaceId}/feed/{channelId}/del']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/feed/{channelId}/del']['delete']['res'][200]> {
public static feedDeleteChannel(data: $OpenApiTs['/workspace/{workspaceId}/feed/{channelId}']['delete']['req']): CancelablePromise<$OpenApiTs['/workspace/{workspaceId}/feed/{channelId}']['delete']['res'][200]> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/workspace/{workspaceId}/feed/{channelId}/del',
url: '/workspace/{workspaceId}/feed/{channelId}',
path: {
workspaceId: data.workspaceId,
channelId: data.channelId

View File

@ -49,9 +49,6 @@ export type $OpenApiTs = {
workspace: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
}>;
};
@ -88,9 +85,6 @@ export type $OpenApiTs = {
workspace: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
}>;
};
@ -128,9 +122,6 @@ export type $OpenApiTs = {
workspace: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
}>;
};
@ -166,9 +157,6 @@ export type $OpenApiTs = {
workspace: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
}>;
};
@ -202,9 +190,6 @@ export type $OpenApiTs = {
workspace: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
}>;
};
@ -226,14 +211,11 @@ export type $OpenApiTs = {
200: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
};
};
};
'/workspace//{workspaceId}/del': {
'/workspace//{workspaceId}': {
delete: {
req: {
workspaceId: string;
@ -271,30 +253,6 @@ export type $OpenApiTs = {
};
};
};
'/workspace//{workspaceId}/updateSettings': {
post: {
req: {
requestBody: {
settings: {
[key: string]: unknown;
};
};
workspaceId: string;
};
res: {
/**
* Successful response
*/
200: {
id: string;
name: string;
settings: {
[key: string]: unknown;
};
};
};
};
};
'/workspace//{workspaceId}/invite': {
post: {
req: {
@ -734,7 +692,7 @@ export type $OpenApiTs = {
};
};
};
'/workspace/{workspaceId}/monitor/{monitorId}/get': {
'/workspace/{workspaceId}/monitor/{monitorId}': {
get: {
req: {
monitorId: string;
@ -764,43 +722,9 @@ export type $OpenApiTs = {
} | null;
};
};
};
'/monitor/getPublicInfo': {
post: {
delete: {
req: {
requestBody: {
monitorIds: Array<(string)>;
};
};
res: {
/**
* Successful response
*/
200: Array<{
id: string;
name: string;
type: string;
trendingMode: boolean;
}>;
};
};
};
'/workspace/{workspaceId}/monitor/upsert': {
post: {
req: {
requestBody: {
id?: string;
name: string;
type: string;
active?: boolean;
interval?: number;
maxRetries?: number;
trendingMode?: boolean;
notificationIds?: Array<(string)>;
payload: {
[key: string]: unknown;
};
};
monitorId: string;
workspaceId: string;
};
res: {
@ -825,10 +749,41 @@ export type $OpenApiTs = {
};
};
};
'/workspace/{workspaceId}/monitor/{monitorId}/del': {
delete: {
'/monitor/getPublicInfo': {
post: {
req: {
monitorId: string;
requestBody: {
monitorIds: Array<(string)>;
};
};
res: {
/**
* Successful response
*/
200: Array<{
id: string;
name: string;
type: string;
}>;
};
};
};
'/workspace/{workspaceId}/monitor/upsert': {
post: {
req: {
requestBody: {
id?: string;
name: string;
type: string;
active?: boolean;
interval?: number;
maxRetries?: number;
trendingMode?: boolean;
notificationIds?: Array<(string)>;
payload: {
[key: string]: unknown;
};
};
workspaceId: string;
};
res: {
@ -921,42 +876,6 @@ export type $OpenApiTs = {
};
};
};
'/workspace/{workspaceId}/monitor/{monitorId}/publicSummary': {
get: {
req: {
monitorId: string;
workspaceId: string;
};
res: {
/**
* Successful response
*/
200: Array<{
day: string;
totalCount: number;
upCount: number;
upRate: number;
}>;
};
};
};
'/workspace/{workspaceId}/monitor/{monitorId}/publicData': {
get: {
req: {
monitorId: string;
workspaceId: string;
};
res: {
/**
* Successful response
*/
200: Array<{
value: number;
createdAt: string;
}>;
};
};
};
'/workspace/{workspaceId}/monitor/{monitorId}/dataMetrics': {
get: {
req: {
@ -1465,14 +1384,13 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
createdAt: string;
updatedAt: string;
}>;
};
};
};
'/workspace/{workspaceId}/survey/{surveyId}/get': {
'/workspace/{workspaceId}/survey/{surveyId}': {
get: {
req: {
surveyId: string;
@ -1496,7 +1414,6 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
createdAt: string;
updatedAt: string;
} | null;
@ -1582,7 +1499,6 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
};
workspaceId: string;
};
@ -1604,7 +1520,6 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
createdAt: string;
updatedAt: string;
};
@ -1626,7 +1541,6 @@ export type $OpenApiTs = {
};
feedChannelIds?: Array<(string)>;
feedTemplate?: string;
webhookUrl?: string;
};
surveyId: string;
workspaceId: string;
@ -1649,7 +1563,6 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
createdAt: string;
updatedAt: string;
};
@ -1680,7 +1593,6 @@ export type $OpenApiTs = {
};
feedChannelIds: Array<(string)>;
feedTemplate: string;
webhookUrl: string;
createdAt: string;
updatedAt: string;
};
@ -1908,7 +1820,7 @@ export type $OpenApiTs = {
};
};
};
'/workspace/{workspaceId}/feed/{channelId}/del': {
'/workspace/{workspaceId}/feed/{channelId}': {
delete: {
req: {
channelId: string;

View File

@ -1,6 +1,6 @@
{
"name": "tianji-client-react",
"version": "1.0.1",
"version": "1.0.0",
"description": "",
"main": "lib/index.js",
"scripts": {

View File

@ -6,7 +6,7 @@ import {
} from 'tianji-client-sdk';
type SurveyInfo =
openApiClient.$OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}/get']['get']['res']['200'];
openApiClient.$OpenApiTs['/workspace/{workspaceId}/survey/{surveyId}']['get']['res']['200'];
interface UseTianjiSurveyOptions {
baseUrl?: string;

View File

@ -135,8 +135,8 @@ importers:
specifier: ^3.3.4
version: 3.3.4(react-hook-form@7.51.1(react@18.2.0))
'@i18next-toolkit/react':
specifier: 2.0.0-rc.5
version: 2.0.0-rc.5(@types/react@18.2.78)(buffer@6.0.3)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
specifier: ^1.1.0
version: 1.1.0(@types/react@18.2.78)(buffer@6.0.3)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@loadable/component':
specifier: ^5.16.3
version: 5.16.3(react@18.2.0)
@ -275,9 +275,6 @@ importers:
lucide-react:
specifier: ^0.358.0
version: 0.358.0(react@18.2.0)
md5:
specifier: ^2.3.0
version: 2.3.0
millify:
specifier: ^6.1.0
version: 6.1.0
@ -381,9 +378,6 @@ importers:
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
'@types/md5':
specifier: ^2.3.5
version: 2.3.5
'@types/react':
specifier: ^18.2.22
version: 18.2.78
@ -441,9 +435,6 @@ importers:
'@auth/express':
specifier: ^0.5.5
version: 0.5.6(express@4.18.2)(nodemailer@6.9.8)
'@lemonsqueezy/lemonsqueezy.js':
specifier: ^3.3.1
version: 3.3.1
'@paralleldrive/cuid2':
specifier: ^2.2.2
version: 2.2.2
@ -495,9 +486,6 @@ importers:
dotenv:
specifier: ^16.3.1
version: 16.3.1
easy-currency-symbol:
specifier: ^1.0.1
version: 1.0.1
express:
specifier: ^4.18.2
version: 4.18.2
@ -543,15 +531,15 @@ importers:
nodemailer:
specifier: ^6.9.8
version: 6.9.8
p-map:
specifier: 4.0.0
version: 4.0.0
passport:
specifier: ^0.7.0
version: 0.7.0
passport-jwt:
specifier: ^4.0.1
version: 4.0.1
ping:
specifier: ^0.4.4
version: 0.4.4
prom-client:
specifier: ^15.1.3
version: 15.1.3
puppeteer:
specifier: 23.4.1
version: 23.4.1(typescript@5.5.4)
@ -631,6 +619,12 @@ importers:
'@types/nodemailer':
specifier: ^6.4.11
version: 6.4.11
'@types/passport':
specifier: ^1.0.12
version: 1.0.12
'@types/passport-jwt':
specifier: ^3.0.9
version: 3.0.9
'@types/ping':
specifier: ^0.4.2
version: 0.4.2
@ -652,6 +646,9 @@ importers:
execa:
specifier: ^5.1.1
version: 5.1.1
p-map:
specifier: 4.0.0
version: 4.0.0
prisma:
specifier: 5.14.0
version: 5.14.0
@ -2251,14 +2248,8 @@ packages:
'@i18next-toolkit/extractor@1.1.0':
resolution: {integrity: sha512-USq83a1XKKCRGqlaKBoNRuCImD1IDFCHMgDHs9686v3IpZ2wQdj/e11+cPaGX1UIjndZKULdQq4b0aZJyMrBfg==}
'@i18next-toolkit/react-core@1.1.0':
resolution: {integrity: sha512-PkuBaIY8jLS0QKy1sjj0g0XAC7zLIDM8ckI5VZGY3feiCPjV6ZdFsuC3cJJwRH+LgOCTj49LurhBii5UXGBlVQ==}
peerDependencies:
'@types/react': ^18.2.55
react: ^18.2.0
'@i18next-toolkit/react@2.0.0-rc.5':
resolution: {integrity: sha512-ZiQaLwS3jnYgFrotDTPbcF74Wbs0JNw0DDouuY+qNegUt5QxLBf/sCymQsglt9C9u2KOU+2CjS3ZP5NWlRknOg==}
'@i18next-toolkit/react@1.1.0':
resolution: {integrity: sha512-S9HFkBwCukCwRR18P4yhskzoBJwIJ2W182GQ9u5H69Guj3Sg4Lm0ghGk7VVALS4z3XmQNcJBzA+LWmLB2X5hIQ==}
peerDependencies:
'@types/react': ^18.2.55
react: ^18.2.0
@ -2331,10 +2322,6 @@ packages:
'@leichtgewicht/ip-codec@2.0.5':
resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==}
'@lemonsqueezy/lemonsqueezy.js@3.3.1':
resolution: {integrity: sha512-gM/FdNsK3BlrD6JRrhmiyqBXQsCpzSUdKSoZwJMQfXqfqcK321og+uMssc6HYcygUMrGvPnNJyJ1RqZPFDrgtg==}
engines: {node: '>=20'}
'@ljharb/through@2.3.11':
resolution: {integrity: sha512-ccfcIDlogiXNq5KcbAwbaO7lMh3Tm1i3khMPYpxlK8hH/W53zN81KM9coerRLOnTGu3nfXIniAmQbRI9OxbC0w==}
engines: {node: '>= 0.4'}
@ -2439,28 +2426,24 @@ packages:
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@next/swc-linux-arm64-musl@14.1.3':
resolution: {integrity: sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@next/swc-linux-x64-gnu@14.1.3':
resolution: {integrity: sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@next/swc-linux-x64-musl@14.1.3':
resolution: {integrity: sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@next/swc-win32-arm64-msvc@14.1.3':
resolution: {integrity: sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==}
@ -3345,7 +3328,6 @@ packages:
resolution: {integrity: sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==}
cpu: [arm]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm-gnueabihf@4.9.5':
resolution: {integrity: sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==}
@ -3356,79 +3338,66 @@ packages:
resolution: {integrity: sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==}
cpu: [arm]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-gnu@4.19.1':
resolution: {integrity: sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-gnu@4.9.5':
resolution: {integrity: sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-arm64-musl@4.19.1':
resolution: {integrity: sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-arm64-musl@4.9.5':
resolution: {integrity: sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==}
cpu: [arm64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-powerpc64le-gnu@4.19.1':
resolution: {integrity: sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.19.1':
resolution: {integrity: sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-riscv64-gnu@4.9.5':
resolution: {integrity: sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-s390x-gnu@4.19.1':
resolution: {integrity: sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.19.1':
resolution: {integrity: sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-gnu@4.9.5':
resolution: {integrity: sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==}
cpu: [x64]
os: [linux]
libc: [glibc]
'@rollup/rollup-linux-x64-musl@4.19.1':
resolution: {integrity: sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-linux-x64-musl@4.9.5':
resolution: {integrity: sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==}
cpu: [x64]
os: [linux]
libc: [musl]
'@rollup/rollup-win32-arm64-msvc@4.19.1':
resolution: {integrity: sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==}
@ -4286,6 +4255,15 @@ packages:
'@types/parse5@6.0.3':
resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==}
'@types/passport-jwt@3.0.9':
resolution: {integrity: sha512-5XJt+79emfgpuBvBQusUPylFIVtW1QVAAkTRwCbRJAmxUjmLtIqUU6V1ovpnHPu6Qut3mR5Juc+s7kd06roNTg==}
'@types/passport-strategy@0.2.35':
resolution: {integrity: sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==}
'@types/passport@1.0.12':
resolution: {integrity: sha512-QFdJ2TiAEoXfEQSNDISJR1Tm51I78CymqcBa8imbjo6dNNu+l2huDxxbDEIoFIwOSKMkOfHEikyDuZ38WwWsmw==}
'@types/pbf@3.0.5':
resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==}
@ -4933,9 +4911,6 @@ packages:
bing-translate-api@4.0.2:
resolution: {integrity: sha512-JJ8XUehnxzOhHU91oy86xEtp8OOMjVEjCZJX042fKxoO19NNvxJ5omeCcxQNFoPbDqVpBJwqiGVquL0oPdQm1Q==}
bintrees@1.0.2:
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
bl@4.1.0:
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
@ -6223,9 +6198,6 @@ packages:
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
easy-currency-symbol@1.0.1:
resolution: {integrity: sha512-M8ugWXSnV5utVTJd4uLOsXqg/sv8Ca7yrDvbSy01mQ52u2XxNnoJ0+tenh3gYCvcceRRLkG/AzIVP/rdjKGuAg==}
ecdsa-sig-formatter@1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@ -7294,9 +7266,6 @@ packages:
humanize-ms@1.2.1:
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
i18next-browser-languagedetector@8.0.0:
resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==}
i18next-http-backend@2.4.3:
resolution: {integrity: sha512-jo2M03O6n1/DNb51WSQ8PsQ0xEELzLZRdYUTbf17mLw3rVwnJF9hwNgMXvEFSxxb+N8dT+o0vtigA6s5mGWyPA==}
@ -9318,6 +9287,17 @@ packages:
pascal-case@3.1.2:
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
passport-jwt@4.0.1:
resolution: {integrity: sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==}
passport-strategy@1.0.0:
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
engines: {node: '>= 0.4.0'}
passport@0.7.0:
resolution: {integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==}
engines: {node: '>= 0.4.0'}
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@ -9398,6 +9378,9 @@ packages:
pathval@1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
pause@0.0.1:
resolution: {integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==}
pbf@3.2.1:
resolution: {integrity: sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==}
hasBin: true
@ -9943,10 +9926,6 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
prom-client@15.1.3:
resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==}
engines: {node: ^16 || ^18 || >=20}
promise-coalesce@1.1.2:
resolution: {integrity: sha512-zLaJ9b8hnC564fnJH6NFSOGZYYdzrAJn2JUUIwzoQb32fG2QAakpDNM+CZo1km6keXkRXRM+hml1BFAPVnPkxg==}
engines: {node: '>=16'}
@ -10332,7 +10311,6 @@ packages:
react-beautiful-dnd@13.1.1:
resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==}
deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672'
peerDependencies:
react: ^16.8.5 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
@ -11604,9 +11582,6 @@ packages:
tcp-ping@0.1.1:
resolution: {integrity: sha512-7Ed10Ds0hYnF+O1lfiZ2iSZ1bCAj+96Madctebmq7Y1ALPWlBY4YI8C6pCL+UTlshFY5YogixKLpgDP/4BlHrw==}
tdigest@0.1.2:
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
teex@1.0.1:
resolution: {integrity: sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==}
@ -14198,7 +14173,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.24.0
'@babel/types': 7.24.0
debug: 4.3.7
debug: 4.3.6
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -14213,7 +14188,7 @@ snapshots:
'@babel/helper-split-export-declaration': 7.22.6
'@babel/parser': 7.24.0
'@babel/types': 7.24.0
debug: 4.3.7
debug: 4.3.6
globals: 11.12.0
transitivePeerDependencies:
- supports-color
@ -15143,25 +15118,14 @@ snapshots:
transitivePeerDependencies:
- buffer
'@i18next-toolkit/react-core@1.1.0(@types/react@18.2.78)(buffer@6.0.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
'@i18next-toolkit/react@1.1.0(@types/react@18.2.78)(buffer@6.0.3)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@types/react': 18.2.78
crc: 4.3.2(buffer@6.0.3)
i18next: 23.10.0
react: 18.2.0
react-i18next: 14.0.5(i18next@23.10.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
transitivePeerDependencies:
- buffer
- react-dom
- react-native
'@i18next-toolkit/react@2.0.0-rc.5(@types/react@18.2.78)(buffer@6.0.3)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@i18next-toolkit/react-core': 1.1.0(@types/react@18.2.78)(buffer@6.0.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@types/react': 18.2.78
i18next-browser-languagedetector: 8.0.0
i18next-http-backend: 2.4.3(encoding@0.1.13)
react: 18.2.0
react-i18next: 14.0.5(i18next@23.10.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
transitivePeerDependencies:
- buffer
- encoding
@ -15295,8 +15259,6 @@ snapshots:
'@leichtgewicht/ip-codec@2.0.5': {}
'@lemonsqueezy/lemonsqueezy.js@3.3.1': {}
'@ljharb/through@2.3.11':
dependencies:
call-bind: 1.0.7
@ -17949,6 +17911,21 @@ snapshots:
'@types/parse5@6.0.3': {}
'@types/passport-jwt@3.0.9':
dependencies:
'@types/express': 4.17.17
'@types/jsonwebtoken': 9.0.5
'@types/passport-strategy': 0.2.35
'@types/passport-strategy@0.2.35':
dependencies:
'@types/express': 4.17.17
'@types/passport': 1.0.12
'@types/passport@1.0.12':
dependencies:
'@types/express': 4.17.17
'@types/pbf@3.0.5': {}
'@types/ping@0.4.2': {}
@ -18296,13 +18273,13 @@ snapshots:
agent-base@6.0.2:
dependencies:
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
agent-base@7.1.0:
dependencies:
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
@ -18813,8 +18790,6 @@ snapshots:
dependencies:
got: 11.8.6
bintrees@1.0.2: {}
bl@4.1.0:
dependencies:
buffer: 5.7.1
@ -20335,8 +20310,6 @@ snapshots:
eastasianwidth@0.2.0: {}
easy-currency-symbol@1.0.1: {}
ecdsa-sig-formatter@1.0.11:
dependencies:
safe-buffer: 5.2.1
@ -20741,7 +20714,7 @@ snapshots:
extract-zip@2.0.1:
dependencies:
debug: 4.3.7
debug: 4.3.6
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@ -21092,7 +21065,7 @@ snapshots:
dependencies:
basic-ftp: 5.0.3
data-uri-to-buffer: 6.0.1
debug: 4.3.7
debug: 4.3.6
fs-extra: 8.1.0
transitivePeerDependencies:
- supports-color
@ -21686,14 +21659,14 @@ snapshots:
http-proxy-agent@7.0.0:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
@ -21734,7 +21707,7 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
@ -21748,21 +21721,21 @@ snapshots:
https-proxy-agent@7.0.0:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.5:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
transitivePeerDependencies:
- supports-color
@ -21776,10 +21749,6 @@ snapshots:
dependencies:
ms: 2.1.3
i18next-browser-languagedetector@8.0.0:
dependencies:
'@babel/runtime': 7.24.0
i18next-http-backend@2.4.3(encoding@0.1.13):
dependencies:
cross-fetch: 4.0.0(encoding@0.1.13)
@ -23506,7 +23475,7 @@ snapshots:
micromark@3.2.0:
dependencies:
'@types/debug': 4.1.12
debug: 4.3.7
debug: 4.3.6
decode-named-character-reference: 1.0.2
micromark-core-commonmark: 1.1.0
micromark-factory-space: 1.1.0
@ -24231,7 +24200,7 @@ snapshots:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
get-uri: 6.0.2
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.5
@ -24326,6 +24295,19 @@ snapshots:
no-case: 3.0.4
tslib: 2.6.2
passport-jwt@4.0.1:
dependencies:
jsonwebtoken: 9.0.2
passport-strategy: 1.0.0
passport-strategy@1.0.0: {}
passport@0.7.0:
dependencies:
passport-strategy: 1.0.0
pause: 0.0.1
utils-merge: 1.0.1
path-browserify@1.0.1: {}
path-dirname@1.0.2: {}
@ -24387,6 +24369,8 @@ snapshots:
pathval@1.1.1: {}
pause@0.0.1: {}
pbf@3.2.1:
dependencies:
ieee754: 1.2.1
@ -24924,11 +24908,6 @@ snapshots:
progress@2.0.3: {}
prom-client@15.1.3:
dependencies:
'@opentelemetry/api': 1.4.1
tdigest: 0.1.2
promise-coalesce@1.1.2: {}
promise-retry@2.0.1:
@ -24987,7 +24966,7 @@ snapshots:
proxy-agent@6.4.0:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.5
lru-cache: 7.18.3
@ -26975,7 +26954,7 @@ snapshots:
socks-proxy-agent@8.0.2:
dependencies:
agent-base: 7.1.0
debug: 4.3.7
debug: 4.3.6
socks: 2.7.1
transitivePeerDependencies:
- supports-color
@ -27480,10 +27459,6 @@ snapshots:
tcp-ping@0.1.1: {}
tdigest@0.1.2:
dependencies:
bintrees: 1.0.2
teex@1.0.1:
dependencies:
streamx: 2.20.1

View File

@ -1,44 +1,78 @@
module tianji-reporter
go 1.21.1
go 1.22.0
toolchain go1.22.5
require (
github.com/docker/docker v26.1.2+incompatible
github.com/json-iterator/go v1.1.12
github.com/shirou/gopsutil/v3 v3.20.10
github.com/stretchr/testify v1.9.0
k8s.io/api v0.31.1
k8s.io/apimachinery v0.31.1
k8s.io/client-go v0.31.1
)
require (
github.com/Microsoft/go-winio v0.4.14 // indirect
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.4 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
go.opentelemetry.io/otel v1.26.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
go.opentelemetry.io/otel/sdk v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/term v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

View File

@ -8,9 +8,11 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v26.1.2+incompatible h1:UVX5ZOrrfTGZZYEP+ZDq3Xn9PdHNXaSYMFPDumMqG2k=
@ -19,37 +21,81 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM=
github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@ -57,20 +103,32 @@ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2sz
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/shirou/gopsutil/v3 v3.20.10 h1:7zomV9HJv6UGk225YtvEa5+camNLpbua3MAz/GqiVJY=
github.com/shirou/gopsutil/v3 v3.20.10/go.mod h1:igHnfak0qnw1biGeI2qKQvu0ZkwvEkUcCLlYhZzdr/4=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 h1:Xs2Ncz0gNihqu9iosIZ5SkBbWo5T8JhhLJFMQL1qmLI=
@ -98,8 +156,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -109,18 +169,22 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -131,12 +195,36 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

44
reporter/utils/k8s.go Normal file
View File

@ -0,0 +1,44 @@
package utils
import (
"context"
"fmt"
"os"
// corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
// "k8s.io/apimachinery/pkg/util/errors"
)
func GetAllNodeConfig() {
// 加载 kubeconfig
kubeconfig := os.Getenv("KUBECONFIG")
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
panic(err.Error())
}
// 创建客户端
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
panic(err.Error())
}
// 获取所有节点
nodes, err := clientset.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
if err != nil {
panic(err.Error())
}
// 输出节点的设备信息
for _, node := range nodes.Items {
fmt.Printf("Node Name: %s\n", node.Name)
for _, condition := range node.Status.Conditions {
fmt.Printf("Condition Type: %s, Status: %s\n", condition.Type, condition.Status)
}
fmt.Printf("Node's Allocatable Resources: %v\n", node.Status.Allocatable)
fmt.Println("-------------------------------")
}
}

View File

@ -0,0 +1,12 @@
package utils
import (
"fmt"
"testing"
)
func TestGetAllNodeConfig(t *testing.T) {
payload := GetAllNodeConfig()
fmt.Println("{}", payload)
}

View File

@ -14,8 +14,6 @@ import {
LuAreaChart,
LuBellDot,
LuFilePieChart,
LuKanbanSquare,
LuKeyRound,
LuMonitorDot,
LuSearch,
LuServer,
@ -173,22 +171,6 @@ export const CommandPanel: React.FC<CommandPanelProps> = React.memo((props) => {
<LuBellDot className="mr-2 h-4 w-4" />
<span>{t('Notifications')}</span>
</CommandItem>
<CommandItem
onSelect={handleJump({
to: '/settings/apiKey',
})}
>
<LuKeyRound className="mr-2 h-4 w-4" />
<span>{t('Api Key')}</span>
</CommandItem>
<CommandItem
onSelect={handleJump({
to: '/settings/usage',
})}
>
<LuKanbanSquare className="mr-2 h-4 w-4" />
<span>{t('Usage')}</span>
</CommandItem>
</CommandGroup>
</CommandList>
</Command>

View File

@ -1,32 +0,0 @@
import { cn } from '@/utils/style';
import React, { PropsWithChildren } from 'react';
import copy from 'copy-to-clipboard';
import { useEvent } from '@/hooks/useEvent';
import { toast } from 'sonner';
import { useTranslation } from '@i18next-toolkit/react';
interface CopyableTextProps extends PropsWithChildren {
className?: string;
text: string;
}
export const CopyableText: React.FC<CopyableTextProps> = React.memo((props) => {
const { t } = useTranslation();
const handleClick = useEvent(() => {
copy(props.text);
toast.success(t('Copied'));
});
return (
<span
className={cn(
'cursor-pointer select-none rounded bg-white bg-opacity-10 px-2',
'hover:bg-white hover:bg-opacity-20',
props.className
)}
onClick={handleClick}
>
{props.children ?? props.text}
</span>
);
});
CopyableText.displayName = 'CopyableText';

View File

@ -1,61 +0,0 @@
import React from 'react';
import { Card, CardContent, CardHeader } from './ui/card';
import { formatNumber } from '@/utils/common';
import { LuAlertCircle } from 'react-icons/lu';
import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip';
import { useTranslation } from '@i18next-toolkit/react';
import colors from 'tailwindcss/colors';
interface UsageCardProps {
title: string;
current: number;
limit?: number;
}
export const UsageCard: React.FC<UsageCardProps> = React.memo((props) => {
const { title, current, limit } = props;
const { t } = useTranslation();
return (
<Card className="relative h-full w-full overflow-hidden">
{limit && (
<div
className="absolute h-full bg-black bg-opacity-5 dark:bg-white dark:bg-opacity-10"
style={{ width: `${(current / limit) * 100}%` }}
/>
)}
{limit && current > limit && (
<div className="absolute right-2 top-2">
<Tooltip>
<TooltipTrigger>
<LuAlertCircle stroke={colors.red['500']} />
</TooltipTrigger>
<TooltipContent>
<div>
{t(
'Exceeded the limit, please upgrade your plan or your workspace will be paused soon.'
)}
</div>
</TooltipContent>
</Tooltip>
</div>
)}
<CardHeader className="text-muted-foreground">{title}</CardHeader>
<CardContent>
{limit && limit >= 0 ? (
<div>
<span className="text-2xl font-bold">{formatNumber(current)}</span>{' '}
/ <span>{formatNumber(limit)}</span>
</div>
) : (
<div>
<span className="text-2xl font-bold">{formatNumber(current)}</span>{' '}
/ <span></span>
</div>
)}
</CardContent>
</Card>
);
});
UsageCard.displayName = 'UsageCard';

View File

@ -1,166 +0,0 @@
import { Check } from 'lucide-react';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import React from 'react';
import { useTranslation } from '@i18next-toolkit/react';
import { useEvent } from '@/hooks/useEvent';
import { defaultErrorHandler, trpc } from '@/api/trpc';
import { useCurrentWorkspaceId } from '@/store/user';
import { cn } from '@/utils/style';
import { Alert, AlertDescription, AlertTitle } from '../ui/alert';
import { LuInfo } from 'react-icons/lu';
interface SubscriptionSelectionProps {
tier: 'FREE' | 'PRO' | 'TEAM' | 'UNLIMITED' | undefined;
}
export const SubscriptionSelection: React.FC<SubscriptionSelectionProps> =
React.memo((props) => {
const { tier } = props;
const workspaceId = useCurrentWorkspaceId();
const { t } = useTranslation();
const checkoutMutation = trpc.billing.checkout.useMutation({
onError: defaultErrorHandler,
});
const handleCheckoutSubscribe = useEvent(
async (tier: 'free' | 'pro' | 'team') => {
const { url } = await checkoutMutation.mutateAsync({
workspaceId,
tier,
redirectUrl: location.href,
});
location.href = url;
}
);
const plans = [
{
id: 'FREE',
name: t('Free'),
price: 0,
features: [
t('Basic trial'),
t('Basic Usage'),
t('Up to 3 websites'),
t('Up to 3 surveys'),
t('Up to 3 feed channels'),
t('100K website events per month'),
t('100K monitor execution per month'),
t('10K feed event per month'),
t('Discord Community Support'),
],
onClick: () => handleCheckoutSubscribe('free'),
},
{
id: 'PRO',
name: 'Pro',
price: 19.99,
features: [
t('Sufficient for most situations'),
t('Priority access to advanced features'),
t('Up to 10 websites'),
t('Up to 20 surveys'),
t('Up to 20 feed channels'),
t('1M website events per month'),
t('1M monitor execution per month'),
t('100K feed events per month'),
t('Discord Community Support'),
],
onClick: () => handleCheckoutSubscribe('pro'),
},
{
id: 'TEAM',
name: 'Team',
price: 99.99,
features: [
t('Fully sufficient'),
t('Priority access to advanced features'),
t('Unlimited websites'),
t('Unlimited surveys'),
t('Unlimited feed channels'),
t('20M website events per month'),
t('20M monitor execution per month'),
t('1M feed events per month'),
t('Priority email support'),
],
onClick: () => handleCheckoutSubscribe('team'),
},
];
return (
<div className="container mx-auto px-4 py-8">
<h1 className="mb-8 text-center text-3xl font-bold">
{t('Subscription Plan')}
</h1>
<Alert className="mb-4">
<LuInfo className="h-4 w-4" />
<AlertTitle>{t('Current Plan')}</AlertTitle>
<AlertDescription>
{t('Your Current Plan is:')}{' '}
<span className="font-bold">{tier}</span>
</AlertDescription>
</Alert>
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
{plans.map((plan) => {
const isCurrent = plan.id === tier;
return (
<Card
key={plan.name}
className={cn('flex flex-col', isCurrent && 'border-primary')}
>
<CardHeader>
<CardTitle>{plan.name}</CardTitle>
<CardDescription>${plan.price} per month</CardDescription>
</CardHeader>
<CardContent className="flex-grow">
<ul className="space-y-2">
{plan.features.map((feature) => (
<li key={feature} className="flex items-center">
<Check className="mr-2 h-4 w-4 text-green-500" />
{feature}
</li>
))}
</ul>
</CardContent>
<CardFooter>
{isCurrent ? (
<Button className="w-full" disabled variant="outline">
{t('Current')}
</Button>
) : (
<Button
className="w-full"
disabled={checkoutMutation.isLoading}
onClick={plan.onClick}
>
{t('{{action}} to {{plan}}', {
action:
plans.indexOf(plan) <
plans.findIndex((p) => p.id === tier)
? t('Downgrade')
: t('Upgrade'),
plan: plan.name,
})}
</Button>
)}
</CardFooter>
</Card>
);
})}
</div>
</div>
);
});
SubscriptionSelection.displayName = 'SubscriptionSelection';

View File

@ -3,46 +3,42 @@ import React from 'react';
import { useTranslation } from '@i18next-toolkit/react';
import { CodeExample } from '../CodeExample';
interface FeedApiGuideProps {
channelId: string;
webhookSignature?: string;
}
export const FeedApiGuide: React.FC<FeedApiGuideProps> = React.memo((props) => {
const { t } = useTranslation();
export const FeedApiGuide: React.FC<{ channelId: string }> = React.memo(
(props) => {
const { t } = useTranslation();
return (
<Card className="w-full overflow-hidden">
<CardHeader>
<div>{t('You can send a message to this channel with:')}</div>
</CardHeader>
<CardContent className="flex w-full flex-col gap-5 overflow-hidden">
<CodeExample
example={{
curl: {
label: 'curl',
code: generateCurlCode(props.channelId, props.webhookSignature),
},
fetch: {
label: 'fetch',
code: generateFetchCode(props.channelId, props.webhookSignature),
},
}}
/>
return (
<Card className="w-full overflow-hidden">
<CardHeader>
<div>{t('You can send a message to this channel with:')}</div>
</CardHeader>
<CardContent className="flex w-full flex-col gap-5 overflow-hidden">
<CodeExample
example={{
curl: {
label: 'curl',
code: generateCurlCode(props.channelId),
},
fetch: {
label: 'fetch',
code: generateFetchCode(props.channelId),
},
}}
/>
<div className="pl-2 font-bold">{t('OR')}</div>
<div className="pl-2 font-bold">{t('OR')}</div>
<div>{t('Integrate with third party with webhook')}</div>
</CardContent>
</Card>
);
});
<div>{t('Integrate with third party with webhook')}</div>
</CardContent>
</Card>
);
}
);
FeedApiGuide.displayName = 'FeedApiGuide';
function generateCurlCode(channelId: string, webhookSignature?: string) {
if (webhookSignature) {
return `curl -X POST ${window.location.origin}/open/feed/${channelId}/send \\
function generateCurlCode(channelId: string) {
const code = `curl -X POST ${window.location.origin}/open/feed/${channelId}/send \\
-H "Content-Type: application/json" \\
-H "X-Webhook-Signature: ${webhookSignature}" \\
-d '{
"eventName": "test name",
"eventContent": "test content",
@ -50,38 +46,12 @@ function generateCurlCode(channelId: string, webhookSignature?: string) {
"source": "custom",
"important": false
}'`;
}
return `curl -X POST ${window.location.origin}/open/feed/${channelId}/send \\
-H "Content-Type: application/json" \\
-d '{
"eventName": "test name",
"eventContent": "test content",
"tags": ["test"],
"source": "custom",
"important": false
}'`;
return code;
}
function generateFetchCode(channelId: string, webhookSignature?: string) {
if (webhookSignature) {
return `fetch('${window.location.origin}/open/feed/${channelId}/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': '${webhookSignature}'
},
body: JSON.stringify({
eventName: 'test name',
eventContent: 'test content',
tags: ['test'],
source: 'custom',
important: false,
})
})`;
}
return `fetch('${window.location.origin}/open/feed/${channelId}/send', {
function generateFetchCode(channelId: string) {
const code = `fetch('${window.location.origin}/open/feed/${channelId}/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@ -94,4 +64,6 @@ function generateFetchCode(channelId: string, webhookSignature?: string) {
important: false,
})
})`;
return code;
}

View File

@ -24,13 +24,9 @@ import {
SelectValue,
} from '../ui/select';
import { NotificationPicker } from '../notification/NotificationPicker';
import { LuRefreshCcw } from 'react-icons/lu';
import md5 from 'md5';
import dayjs from 'dayjs';
const addFormSchema = z.object({
name: z.string(),
webhookSignature: z.string().default(''),
notificationIds: z.array(z.string()).default([]),
notifyFrequency: z.enum(['none', 'event', 'day', 'week', 'month']),
});
@ -49,7 +45,6 @@ export const FeedChannelEditForm: React.FC<FeedChannelEditFormProps> =
resolver: zodResolver(addFormSchema),
defaultValues: props.defaultValues ?? {
name: 'New Channel',
webhookSignature: '',
notificationIds: [],
notifyFrequency: 'none',
},
@ -84,38 +79,6 @@ export const FeedChannelEditForm: React.FC<FeedChannelEditFormProps> =
)}
/>
<FormField
control={form.control}
name="webhookSignature"
render={({ field }) => (
<FormItem>
<FormLabel optional={true}>
{t('Webhook Signature')}
</FormLabel>
<FormControl>
<div className="flex">
<Input className="rounded-r-none" {...field} />
<Button
className="rounded-l-none"
type="button"
Icon={LuRefreshCcw}
onClick={() => {
form.setValue(
'webhookSignature',
md5(dayjs().valueOf().toString())
);
}}
/>
</div>
</FormControl>
<FormDescription>
{t('Optional, Webhook Signature for Incoming Webhook')}
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="notificationIds"

View File

@ -4,11 +4,9 @@ import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
import { CodeBlock } from '../CodeBlock';
import { useTranslation } from '@i18next-toolkit/react';
import { SiSentry } from 'react-icons/si';
import { FaStripe } from 'react-icons/fa6';
export const FeedIntegration: React.FC<{
feedId: string;
webhookSignature: string;
}> = React.memo((props) => {
const { t } = useTranslation();
@ -59,22 +57,6 @@ export const FeedIntegration: React.FC<{
}
/>
<FeedIntegrationItem
icon={<FaStripe size={32} />}
label="Stripe"
content={
<div>
<div className="text-lg font-bold">{t('Receive Webhooks')}</div>
<div>{t('Add sentry webhook with url')}:</div>
<CodeBlock
code={`${window.location.origin}/open/feed/${props.feedId}/stripe`}
/>
</div>
}
/>
<div onClick={() => window.open('/feed/playground', '_blank')}>
<FeedIntegrationItemTrigger
icon={<LuTestTube2 size={32} />}
@ -93,7 +75,7 @@ export const FeedIntegration: React.FC<{
<CodeBlock
code={`POST ${window.location.origin}/open/feed/${props.feedId}/send
${props.webhookSignature ? `\nHeader:\nX-Webhook-Signature: ${props.webhookSignature}\n` : ''}
Body
{
eventName: "",

View File

@ -23,7 +23,7 @@ import {
useUserInfo,
useUserStore,
} from '@/store/user';
import { languages } from '@/utils/i18n';
import { languages } from '@/utils/constants';
import { useTranslation, setLanguage } from '@i18next-toolkit/react';
import { useNavigate } from '@tanstack/react-router';
import { version } from '@/utils/env';

View File

@ -61,10 +61,6 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const testNotifyScriptMutation = trpc.monitor.testNotifyScript.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const trpcUtils = trpc.useContext();
@ -233,15 +229,6 @@ export const MonitorInfo: React.FC<MonitorInfoProps> = React.memo((props) => {
label: t('Show Badge'),
onClick: () => setShowBadge(true),
},
{
key: 'testNotify',
label: t('Test Notify'),
onClick: () =>
testNotifyScriptMutation.mutateAsync({
workspaceId,
monitorId,
}),
},
{
type: 'divider',
},

View File

@ -2,7 +2,6 @@ import { AppRouterOutput, trpc } from '@/api/trpc';
import React, { useMemo, useReducer } from 'react';
import { bodySchema } from './schema';
import { Empty } from 'antd';
import { Separator } from '@/components/ui/separator';
import { useTranslation } from '@i18next-toolkit/react';
import { cn } from '@/utils/style';
import {
@ -37,13 +36,12 @@ export const StatusPageBody: React.FC<StatusPageBodyProps> = React.memo(
}, [info.body]);
return (
<div className="rounded-lg border border-gray-200/80 dark:border-gray-700/25">
<div>
{body.groups.map((group) => (
<div key={group.key} className="m-4 rounded-lg bg-neutral-500/15">
<div className="ml-4 pl-2.5 pt-2.5 text-lg font-semibold">
{group.title}
</div>
<div className="flex flex-col gap-2 rounded-md p-2.5">
<div key={group.key} className="mb-6">
<div className="mb-2 text-lg font-semibold">{group.title}</div>
<div className="flex flex-col gap-4 rounded-md border border-gray-200 p-2.5 dark:border-gray-700">
{group.children.length === 0 && (
<Empty description={t('No any monitor has been set')} />
)}
@ -51,14 +49,12 @@ export const StatusPageBody: React.FC<StatusPageBodyProps> = React.memo(
{group.children.map((item) => {
if (item.type === 'monitor') {
return (
<React.Fragment key={item.key}>
<Separator />
<StatusItemMonitor
workspaceId={props.workspaceId}
monitorId={item.id}
showCurrent={item.showCurrent ?? false}
/>
</React.Fragment>
<StatusItemMonitor
key={item.key}
workspaceId={props.workspaceId}
monitorId={item.id}
showCurrent={item.showCurrent ?? false}
/>
);
}
@ -125,7 +121,7 @@ export const StatusItemMonitor: React.FC<{
<div>
<span
className={cn(
'text-white inline-block min-w-[62px] rounded-lg p-0.5 text-center font-semibold',
'inline-block min-w-[62px] rounded-full p-0.5 text-center text-white',
getStatusBgColorClassName(summaryStatus)
)}
>

View File

@ -1,199 +0,0 @@
import React, { useMemo } from 'react';
import { cn } from '@/utils/style';
import { bodySchema } from './schema';
import { LuCheckCircle2, LuCircleSlash, LuAlertCircle } from 'react-icons/lu';
import { AppRouterOutput, trpc } from '../../../api/trpc';
import { getMonitorProvider, getProviderDisplay } from '../provider';
import { takeRight, last } from 'lodash-es';
import dayjs from 'dayjs';
import { IconType } from 'react-icons';
import { useTranslation } from '@i18next-toolkit/react';
interface StatusPageHeaderProps {
info: NonNullable<AppRouterOutput['monitor']['getPageInfo']>;
workspaceId: string;
}
interface ContextItem {
id: string;
groupId: string;
groupName: string;
}
type StatusType = 'operational' | 'degraded' | 'offline' | 'unknown';
export const StatusPageHeader: React.FC<StatusPageHeaderProps> = React.memo(
({ info, workspaceId }) => {
const { t } = useTranslation();
const body = useMemo(() => {
const res = bodySchema.safeParse(info.body);
return res.success ? res.data : { groups: [] };
}, [info.body]);
const monitorContexts = useMemo(() => {
const contexts: ContextItem[] = [];
body.groups.forEach((group) => {
group.children.forEach((item) => {
if (item.type === 'monitor') {
contexts.push({
id: item.id,
groupId: group.key,
groupName: group.title,
});
}
});
});
if (Array.isArray(info.monitorList)) {
info.monitorList.forEach((monitor) => {
contexts.push({
id: monitor.id,
groupId: 'deprecated',
groupName: 'Legacy Monitors',
});
});
}
return contexts;
}, [body, info.monitorList]);
const recentDataQueries = monitorContexts.map((context) => {
const { data: recentData = [] } = trpc.monitor.recentData.useQuery({
workspaceId,
monitorId: context.id,
take: 1,
});
const items = useMemo(() => {
return takeRight(
[...Array.from({ length: 1 }).map(() => null), ...recentData],
1
);
}, [recentData]);
const provider = useMemo(
() => getMonitorProvider(context.id),
[context.id]
);
const latestStatus = useMemo(() => {
const latestItem = last(items);
if (!latestItem) {
return 'none';
}
const { value, createdAt } = latestItem;
const { text } = getProviderDisplay(value, provider);
const title = `${dayjs(createdAt).format('YYYY-MM-DD HH:mm')} | ${text}`;
return value < 0
? { status: 'error', title }
: { status: 'health', title };
}, [items, provider]);
return {
id: context.id,
status: latestStatus === 'none' ? undefined : latestStatus.status,
timestamp: dayjs(last(items)?.createdAt).valueOf(),
};
});
const { overallStatus, lastChecked } = useMemo(() => {
let totalCount = 0;
let errorCount = 0;
let latestTimestamp = 0;
recentDataQueries.forEach((query) => {
if (!query) return;
totalCount += 1;
if (query.status != 'health') {
errorCount += 1;
}
if (
!latestTimestamp ||
(query.timestamp && query.timestamp > latestTimestamp)
) {
latestTimestamp = query.timestamp;
}
});
let status: string = 'unknown';
let uprate = ((totalCount - errorCount) / totalCount) * 100;
if (uprate > 90) {
status = 'operational';
} else if (uprate > 50) {
status = 'degraded';
} else if (uprate > 0) {
status = 'offline';
}
return {
overallStatus: status as StatusType,
servicesCount: totalCount,
lastChecked: latestTimestamp,
};
}, [recentDataQueries]);
const statusConfig: Record<
StatusType,
{ text: string; icon: IconType; iconColor: string }
> = {
operational: {
text: t('All Systems Operational'),
icon: LuCheckCircle2,
iconColor: 'text-green-500',
},
degraded: {
text: t('Partial System Outage'),
icon: LuAlertCircle,
iconColor: 'text-yellow-500',
},
offline: {
text: t('Major System Outage'),
icon: LuCircleSlash,
iconColor: 'text-red-500',
},
unknown: {
text: t('Status Unknown'),
icon: LuAlertCircle,
iconColor: 'text-gray-500',
},
};
const config = statusConfig[overallStatus];
const StatusIcon = config.icon;
const formatDate = (date: number) => {
const options: Intl.DateTimeFormatOptions = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
hour12: true,
};
const formatted = new Date(date).toLocaleString('en-US', options);
return `${t('Last updated')} ${formatted}`;
};
return (
<div className="flex flex-col items-center space-y-2">
<StatusIcon
className={cn('h-12 w-12', config.iconColor)}
aria-hidden="true"
/>
<h1 className="pb-2 pt-4 text-4xl font-bold">{config.text}</h1>
{lastChecked && (
<p className="text-md text-gray-600 dark:text-gray-400">
{formatDate(lastChecked)}
</p>
)}
</div>
);
}
);
export default StatusPageHeader;

View File

@ -9,7 +9,6 @@ import clsx from 'clsx';
import { useRequest } from '../../../hooks/useRequest';
import { ColorSchemeSwitcher } from '../../ColorSchemeSwitcher';
import { StatusPageServices } from './Services';
import { StatusPageHeader } from './StatusHeader';
import { useTranslation } from '@i18next-toolkit/react';
import { Link, useNavigate } from '@tanstack/react-router';
import { Helmet } from 'react-helmet';
@ -147,14 +146,8 @@ export const MonitorStatusPage: React.FC<MonitorStatusPageProps> = React.memo(
</div>
)}
{info && (
<div className="my-6">
<StatusPageHeader info={info} workspaceId={info.workspaceId} />
</div>
)}
{/* Desc */}
<div className="mb-6 text-center">
<div className="mb-4">
<MarkdownViewer value={info?.description ?? ''} />
</div>

View File

@ -1,59 +0,0 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/utils/style"
const alertVariants = cva(
"relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
},
},
defaultVariants: {
variant: "default",
},
}
)
const Alert = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
>(({ className, variant, ...props }, ref) => (
<div
ref={ref}
role="alert"
className={cn(alertVariants({ variant }), className)}
{...props}
/>
))
Alert.displayName = "Alert"
const AlertTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h5
ref={ref}
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
{...props}
/>
))
AlertTitle.displayName = "AlertTitle"
const AlertDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("text-sm [&_p]:leading-relaxed", className)}
{...props}
/>
))
AlertDescription.displayName = "AlertDescription"
export { Alert, AlertTitle, AlertDescription }

View File

@ -22,9 +22,6 @@ export function useGlobalConfig(): AppRouterOutput['global']['config'] {
{
staleTime: 1000 * 60 * 60 * 1, // 1 hour
onSuccess(data) {
/**
* Call anonymous telemetry if not disabled
*/
if (data.disableAnonymousTelemetry !== true) {
callAnonymousTelemetry();
}

View File

@ -1,6 +1,6 @@
/** @type {import('@i18next-toolkit/cli').I18nextToolkitConfig} */
const config = {
locales: ['en', 'zh-CN', 'ja-JP', 'fr-FR', 'de-DE', 'pl-PL', 'pt-PT', 'ru-RU'],
locales: ['en', 'zh', 'jp', 'fr', 'de', 'pl', 'pt', 'ru'],
verbose: true,
namespaces: ['translation'],
translator: {

View File

@ -1,3 +0,0 @@
import { initI18N } from './utils/i18n';
initI18N();

View File

@ -1,6 +1,5 @@
import './index.css';
import './styles/global.less';
import './init';
import React from 'react';
import ReactDOM from 'react-dom/client';

View File

@ -23,7 +23,7 @@
"@bytemd/plugin-gfm": "^1.21.0",
"@bytemd/react": "^1.21.0",
"@hookform/resolvers": "^3.3.4",
"@i18next-toolkit/react": "2.0.0-rc.5",
"@i18next-toolkit/react": "^1.1.0",
"@loadable/component": "^5.16.3",
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-alert-dialog": "^1.0.5",
@ -70,7 +70,6 @@
"leaflet": "^1.9.4",
"lodash-es": "^4.17.21",
"lucide-react": "^0.358.0",
"md5": "^2.3.0",
"millify": "^6.1.0",
"next-themes": "^0.2.1",
"pretty-ms": "^9.0.0",
@ -107,7 +106,6 @@
"@types/leaflet": "^1.9.8",
"@types/loadable__component": "^5.13.8",
"@types/lodash-es": "^4.17.12",
"@types/md5": "^2.3.5",
"@types/react": "^18.2.22",
"@types/react-beautiful-dnd": "^13.1.8",
"@types/react-dom": "^18.2.7",

View File

Before

Width:  |  Height:  |  Size: 269 B

After

Width:  |  Height:  |  Size: 269 B

View File

@ -19,14 +19,11 @@
"k17058821": "Website Lighthouse Berichte",
"k172a09c3": "Vorschläge",
"k1777bbf2": "Manuell",
"k1940fd6": "Allgemein",
"k1964b988": "Stopp",
"k1bd89236": "Reporter mit ausführen",
"k1c33c293": "Einstellungen",
"k1d8f92b4": "Tablet",
"k1da4ecc2": "Sie können eine Nachricht an diesen Kanal senden mit:",
"k1eb5b3ed": "Übersicht",
"k1ee0c2ca": "Setzen Sie die Webhook-URL auf <1></1> und halten Sie dieses Fenster aktiv. Sobald Sie fertig sind, beginnen Sie, Webhook-Anfragen hier zu empfangen.",
"k1f6dea0": "Kanalname",
"k2099f2e0": "Anmeldung fehlgeschlagen, bitte überprüfen Sie Ihren Benutzernamen und Ihr Passwort",
"k20edf271": "24 Stunden",
@ -56,15 +53,12 @@
"k2c84fe32": "Feed-Ereigniszähler",
"k2cecf817": "Typ",
"k2dad13e3": "Sprache",
"k2db2c0c5": "Testbenachrichtigung",
"k2e6dbf02": "An E-Mail",
"k2ea8a019": "Überwachen",
"k30b5f01b": "Arbeitsbereiche",
"k30d33d71": "Webhook-Signatur",
"k310fee": "Letzte 30 Tage",
"k32344f64": "Daten löschen",
"k3260f019": "Abmelden",
"k3404b72f": "Neuer Arbeitsbereichsname",
"k340547f0": "Entschuldigung, aber etwas ist schief gelaufen",
"k3471e956": "Neues Passwort wiederholen",
"k34981fea": "Docker treibt auf See und findet seinen Weg nicht. Bitte starten Sie Docker, um wieder auf Kurs zu kommen.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Discord beitreten",
"k3eaab921": "ÜberwachungsListe",
"k3f36e17e": "Twitter folgen",
"k406089a4": "Aktion",
"k406e9ad8": "Bestätigen",
"k41d3ce6c": "Ereignis wiederhergestellt",
"k42347b91": "Website-Ereigniszählung",
@ -100,8 +93,7 @@
"k44186b66": "Zählung",
"k44cad477": "(Aktuell)",
"k45f80a27": "Erweitert",
"k4727e4db": "Ablaufdatum",
"k477b7ee4": "Teilweise Systemausfälle",
"k4738284": "Sie können jede Nachricht in diesen Kanal mit folgendem senden:",
"k47fe1f95": "Fügen Sie diesen Beispielcode zu Ihrem Projekt hinzu",
"k48186ce": "Zurück zur Startseite",
"k4905ed7b": "KEINE",
@ -115,7 +107,6 @@
"k4de48e75": "Maximale Wiederholungen",
"k4e08cf58": "Detailnummer anzeigen",
"k4eea9393": "Profil",
"k4f182a7c": "Wichtige Systemausfälle",
"k4fc2b5b": "Bild",
"k4fe1b4de": "Telemetrie",
"k505c2733": "Bericht erstellen",
@ -132,12 +123,9 @@
"k58267a45": "Quelle",
"k58f90514": "Bot-Token",
"k593cf342": "Sind Sie sicher, diesen Monitor zu löschen?",
"k5a782f4b": "Website-Anzahl",
"k5a839f71": "Betriebszeit",
"k5b5be0d4": "Aktuelle Rolle",
"k5c18db28": "Statusseiteninformationen ändern",
"k5d00536d": "Kopiert",
"k5d49d751": "Neuer API-Schlüssel wurde in Ihre Zwischenablage kopiert!",
"k5eb87a8b": "Start",
"k5ec0de4": "Für die HTTPS-Überwachung werden bei Zuweisung einer Benachrichtigungsmethode Benachrichtigungen 1, 3, 7 und 14 Tage vor Ablauf gesendet.",
"k5ecf04b0": "Ansicht",
@ -147,7 +135,6 @@
"k62e19375": "Letzte Aktualisierung: {{date}}",
"k6488f302": "Optional",
"k659b065": "Zum Beispiel: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Anforderungsinhalt",
"k67c5a895": "Gestern",
"k683be220": "Ausführen",
"k691b7170": "Gestoppt",
@ -160,11 +147,9 @@
"k6e96fc3": "Formularinfo",
"k6ea11aff": "Holen!",
"k6f15bcc3": "Host",
"k71067412": "Optional, Webhook-Signatur für eingehenden Webhook",
"k721589c1": "Heute",
"k7247683c": "Arbeitsbereich löschen",
"k7350bd93": "Gleichzeitig können wir es auch in einigen Client-Seiten-Anwendungsszenarien verwenden, wie z.B. das Sammeln der Häufigkeit der CLI-Nutzung, das Sammeln der Installation von selbst gehosteten Apps und so weiter.",
"k736f3e4c": "Kopieren als",
"k75581e13": "Kreditkarte",
"k75bfaaa6": "Fügen Sie diesen Code in das Kopf-Skript Ihrer Website ein",
"k763816ac": "Vorschau",
@ -172,17 +157,16 @@
"k78b1ef6a": "Eingeben",
"k7927b824": "Sind Sie sicher, alle Offline-Knoten zu löschen?",
"k7a132ce8": "Entschuldigung, aber diese Seite wurde nicht gefunden",
"k7a15497a": "Echtzeit",
"k7ac44a6e": "Sitzungsschlüssel",
"k7b74a43f": "Besucher",
"k7b75e24c": "Integration",
"k7b9aa48c": "Inhalt",
"k7cac602a": "Status",
"k7d8cd81c": "URL kopieren",
"k7e0360fd": "Es wurde keine Gruppe erstellt, klicken Sie auf die Schaltfläche, um eine zu erstellen",
"k7e61b1af": "Arbeitsbereich auswählen",
"k7f01b47c": "Prüfprotokoll",
"k7f03a704": "Denken Sie daran, keine Daten mit application/json zu senden",
"k7f29bae5": "Seitenaufrufe",
"k8037cc6b": "Server",
"k816ce026": "Herunterladen",
"k819633bc": "Zur Speicherung verwenden",
@ -192,7 +176,6 @@
"k84ce1618": "(24 Stunden)",
"k84e82947": "{{num}} Ereignisse gelöscht",
"k85344b23": "Laden",
"k85a116ee": "Webhook-URL",
"k85c5fd4c": "Noch kein Monitor eingerichtet",
"k85db19da": "Noch kein Feed-Kanal vorhanden. Verwenden Sie die Feed-Funktion, um alle Ereignisse aus dem Netzwerk oder Ihrem eigenen Dienst zu empfangen.",
"k873c90e6": "Anzeigelabel",
@ -205,7 +188,6 @@
"k88d2647b": "Webseite",
"k89056082": "(30 Tage)",
"k892f84b6": "Aktuelle Benutzerinformationen können nicht abgerufen werden",
"k895cafe1": "Optional, Webhook-URL zum Senden der Umfrage-Payload",
"k899fd0cd": "Ports",
"k89d54f7a": "Überwachung der Ausführungszählung",
"k8a1deb63": "Mitglieder",
@ -225,10 +207,7 @@
"k90b603b8": "Duplizieren",
"k90b668e5": "Letzte 24 Stunden",
"k93374bc9": "Website löschen",
"k93458b98": "Spielplatz",
"k951a939a": "Akzeptierte Zählung der Website",
"k95f932a": "Warten derzeit auf eine neue Anfrage vom Remote-Server",
"k97b02874": "Seitenanzahl",
"k98f433ee": "Reporter herunterladen von",
"k9991c290": "Gemeinschaft",
"k9a272ecf": "Sind das Ihre Server?",
@ -254,7 +233,6 @@
"ka6ee7455": "Website-ID",
"ka71c12e1": "Die beiden Passwörter stimmen nicht überein",
"ka765ad32": "Benachrichtigung",
"ka7d8617e": "Feed-Kanalanzahl",
"ka7fe5937": "Festplattenlesen/-schreiben",
"ka8e41156": "Suche und schneller Sprung",
"ka90bc019": "Deinstallieren",
@ -276,7 +254,6 @@
"kb0e351e0": "Aktualisiert",
"kb114a2e8": "Veraltet",
"kb15a6374": "Sie können Ihre Statusseite in Ihrer eigenen Domain konfigurieren, zum Beispiel: status.beispiel.com",
"kb2dded49": "Schlüssel",
"kb320aac4": "Überwacht seit {{dayNum}} Tagen",
"kb35cde91": "Suche",
"kb35d71ed": "ODER",
@ -284,7 +261,6 @@
"kb5673707": "Letzte 7 Tage",
"kb659c1bc": "Zert. Ablauf",
"kb6d350b6": "Feed-Kanäle",
"kb7bf8869": "API-Schlüssel",
"kb7fa344a": "Wählen Sie einen Feed-Kanal zum Senden aus",
"kb8de8c50": "BCC",
"kbb31d3db": "Statistikdatum",
@ -320,16 +296,13 @@
"kcc9c1bff": "Jede Woche",
"kccaa732a": "Keine aufeinanderfolgenden Bindestriche",
"kccb42483": "Passwort",
"kcd56f27b": "Zuletzt aktualisiert",
"kcd643ef3": "Lade...",
"kce77d0c1": "Zeitzone",
"kcff78587": "Zuletzt verwendet am",
"kd005f7a8": "Alle Feeds werden entfernt",
"kd031b383": "Ansichten",
"kd044d5d4": "Session",
"kd092de58": "Aktueller Arbeitsbereich:",
"kd1f7e695": "Abmelden bestätigen",
"kd211e2d4": "Versionsseite",
"kd25f123a": "Status unbekannt",
"kd2a7ad83": "Feed-Vorlage",
"kd3262a4a": "Konfig",
"kd3396544": "Allgemein werden wir ein ein Pixel großes leeres Bild verwenden, sodass es die normale Nutzung des Benutzers nicht beeinträchtigt.",
@ -338,18 +311,14 @@
"kd7279fa6": "Code",
"kd7985726": "{{num}} Benutzer",
"kd92fa3e7": "Host-Name",
"kdaa6ae2b": "Überwachungsanzahl",
"kdaff25a6": "Zeige den neuesten Wert",
"kdb61adbb": "Offline verbergen",
"kdbadcf43": "Alle Systeme betriebsbereit",
"kdbe222b": "API-Schlüssel",
"kdc10ee1a": "Erstellen Sie einen neuen Arbeitsbereich, um mit Teammitgliedern zusammenzuarbeiten.",
"kdc15c5d": "Daten",
"kdc1bf80e": "Url ist erforderlich",
"kdc51b5db": "Webseiten",
"kdd44ac01": "Anzuzeigender Telemetrie-Name",
"kdd55936a": "Resolver-Port",
"kde315178": "Umbenennen",
"kde37bc27": "Zurück zum Admin",
"kdeba7706": "Geräte",
"kdeecbfea": "Resolver-Server",
@ -390,7 +359,6 @@
"kf246dd2e": "Es wurde kein Arbeitsbereich gefunden, bitte zuerst erstellen",
"kf3b749ef": "Unterstützt Direktchat / Gruppe / Kanal-Chat-ID",
"kf55495e0": "Speichern",
"kf5c3b616": "Anforderungsheader",
"kf5c9520e": "Noch keine Statusseite vorhanden, Sie können eine neue erstellen, um den Status Ihres Dienstes der Öffentlichkeit anzuzeigen.",
"kf6339d4f": "Verifiziert",
"kf6582ba": "Arbeitsbereich",
@ -406,7 +374,6 @@
"kf97b6f71": "Führen Sie diesen Befehl auf Ihrer Linux-Maschine aus",
"kf9877f28": "Details anzeigen",
"kf9965c19": "Alle Inhalte in diesem Arbeitsbereich werden zerstört und können nicht wiederhergestellt werden.",
"kf9a498c7": "Lighthouse-Bericht abgeschlossen!",
"kfc98929b": "{{num}} Tage",
"kfd33c459": "Kopieren erfolgreich!",
"kfdaf0bb3": "Zuletzt online: {{time}}",

View File

@ -19,14 +19,11 @@
"k17058821": "Website Lighthouse Reports",
"k172a09c3": "Suggestions",
"k1777bbf2": "Manual",
"k1940fd6": "General",
"k1964b988": "Stop",
"k1bd89236": "run reporter with",
"k1c33c293": "Settings",
"k1d8f92b4": "Tablet",
"k1da4ecc2": "You can send a message to this channel with:",
"k1eb5b3ed": "Overview",
"k1ee0c2ca": "Set the webhook URL to <1></1>, and keep this window active. Once done, you will start receiving webhook requests here.",
"k1f6dea0": "Channel Name",
"k2099f2e0": "Login failed, please check your username and password",
"k20edf271": "24h",
@ -56,15 +53,12 @@
"k2c84fe32": "Feed Event Count",
"k2cecf817": "Type",
"k2dad13e3": "Language",
"k2db2c0c5": "Test Notify",
"k2e6dbf02": "To Email",
"k2ea8a019": "Monitor",
"k30b5f01b": "Workspaces",
"k30d33d71": "Webhook Signature",
"k310fee": "Last 30 days",
"k32344f64": "Clear Data",
"k3260f019": "Logout",
"k3404b72f": "New Workspace Name",
"k340547f0": "Sorry, but something went wrong",
"k3471e956": "Repaet New Password",
"k34981fea": "Docker is adrift at sea, unable to find its way. Please start Docker to get back on course.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Join Discord",
"k3eaab921": "Monitor List",
"k3f36e17e": "Follow Twitter",
"k406089a4": "Action",
"k406e9ad8": "Confirm",
"k41d3ce6c": "Event unarchived",
"k42347b91": "Website Event Count",
@ -100,8 +93,7 @@
"k44186b66": "Count",
"k44cad477": "(Current)",
"k45f80a27": "Advanced",
"k4727e4db": "Expired At",
"k477b7ee4": "Partial System Outage",
"k4738284": "You can send a message to this channel with:",
"k47fe1f95": "Add this example code into your project",
"k48186ce": "Back to Homepage",
"k4905ed7b": "NONE",
@ -115,7 +107,6 @@
"k4de48e75": "Max Retries",
"k4e08cf58": "Show Detail Number",
"k4eea9393": "Profile",
"k4f182a7c": "Major System Outage",
"k4fc2b5b": "Image",
"k4fe1b4de": "Telemetry",
"k505c2733": "Create Report",
@ -132,12 +123,9 @@
"k58267a45": "Source",
"k58f90514": "Bot Token",
"k593cf342": "Are you sure you want to delete this monitor?",
"k5a782f4b": "Website Count",
"k5a839f71": "Uptime",
"k5b5be0d4": "Current Role",
"k5c18db28": "Modify Status Page Info",
"k5d00536d": "Copied",
"k5d49d751": "New api key has been copied into your clipboard!",
"k5eb87a8b": "Start",
"k5ec0de4": "For HTTPS monitoring, if any notification method is assigned, notifications will be sent at 1, 3, 7 and 14 days before expiration.",
"k5ecf04b0": "View",
@ -147,7 +135,6 @@
"k62e19375": "Last updated at: {{date}}",
"k6488f302": "Optional",
"k659b065": "For example: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Request Body",
"k67c5a895": "Yesterday",
"k683be220": "Run",
"k691b7170": "Stopped",
@ -160,11 +147,9 @@
"k6e96fc3": "Form Info",
"k6ea11aff": "Get!",
"k6f15bcc3": "Host",
"k71067412": "Optional, Webhook Signature for Incoming Webhook",
"k721589c1": "Today",
"k7247683c": "Delete Workspace",
"k7350bd93": "At the same time, we can also use it in some client-side application scenarios, such as collecting the frequency of cli usage or installation of selfhosted apps, and so on.",
"k736f3e4c": "Copy as",
"k75581e13": "CC",
"k75bfaaa6": "Add this code into your website head script",
"k763816ac": "Preview",
@ -172,17 +157,16 @@
"k78b1ef6a": "Enter",
"k7927b824": "Are you sure to clear all offline nodes?",
"k7a132ce8": "Sorry, but this page is not found",
"k7a15497a": "Realtime",
"k7ac44a6e": "Session Key",
"k7b74a43f": "visitors",
"k7b75e24c": "Integration",
"k7b9aa48c": "Body",
"k7cac602a": "Status",
"k7d8cd81c": "Copy URL",
"k7e0360fd": "No group has been created yet, click button to create one",
"k7e61b1af": "Select Workspace",
"k7f01b47c": "Audit Log",
"k7f03a704": "Dont remember send data with application/json",
"k7f29bae5": "pageview",
"k8037cc6b": "Servers",
"k816ce026": "Download",
"k819633bc": "Use for storage",
@ -192,7 +176,6 @@
"k84ce1618": "(24 hour)",
"k84e82947": "{{num}} events cleared",
"k85344b23": "Load",
"k85a116ee": "Webhook Url",
"k85c5fd4c": "No monitor has been set",
"k85db19da": "No feed channel yet. Use feed feature to receive any event from your network or service.",
"k873c90e6": "Display Label",
@ -205,7 +188,6 @@
"k88d2647b": "Website",
"k89056082": "(30 days)",
"k892f84b6": "Can not get current user info",
"k895cafe1": "Optional, webhook url to send survey payload",
"k899fd0cd": "ports",
"k89d54f7a": "Monitor Execution Count",
"k8a1deb63": "Members",
@ -225,10 +207,7 @@
"k90b603b8": "Duplicate",
"k90b668e5": "Last 24 Hours",
"k93374bc9": "Delete Website",
"k93458b98": "Playground",
"k951a939a": "Website Accepted Count",
"k95f932a": "Currently waiting for a new request from the remote server",
"k97b02874": "Page Count",
"k98f433ee": "Download reporter from",
"k9991c290": "Community",
"k9a272ecf": "Is this your servers?",
@ -254,7 +233,6 @@
"ka6ee7455": "Website ID",
"ka71c12e1": "The two passwords are not consistent",
"ka765ad32": "Notification",
"ka7d8617e": "Feed Channel Count",
"ka7fe5937": "Disk read/write",
"ka8e41156": "Search and quick jump",
"ka90bc019": "Uninstall",
@ -276,7 +254,6 @@
"kb0e351e0": "Refreshed",
"kb114a2e8": "Deprecated",
"kb15a6374": "You can config your status page in your own domain, for example: status.example.com",
"kb2dded49": "Key",
"kb320aac4": "Monitored for {{dayNum}} days",
"kb35cde91": "Search",
"kb35d71ed": "OR",
@ -284,7 +261,6 @@
"kb5673707": "Last 7 days",
"kb659c1bc": "Cert Exp.",
"kb6d350b6": "Feed Channels",
"kb7bf8869": "Api Keys",
"kb7fa344a": "Select Feed Channel for send",
"kb8de8c50": "BCC",
"kbb31d3db": "Statistic Date",
@ -320,16 +296,13 @@
"kcc9c1bff": "Every Week",
"kccaa732a": "No consecutive dashes",
"kccb42483": "Password",
"kcd56f27b": "Last updated",
"kcd643ef3": "Loading...",
"kce77d0c1": "Timezone",
"kcff78587": "Last Use At",
"kd005f7a8": "All feed will be remove",
"kd031b383": "Views",
"kd044d5d4": "session",
"kd092de58": "Current Workspace:",
"kd1f7e695": "Confirm to logout",
"kd211e2d4": "Releases Page",
"kd25f123a": "Status Unknown",
"kd2a7ad83": "Feed Template",
"kd3262a4a": "Config",
"kd3396544": "Generally, we will use a one-pixel blank image so that it will not affect the user's normal use.",
@ -338,18 +311,14 @@
"kd7279fa6": "Code",
"kd7985726": "{{num}} users",
"kd92fa3e7": "Host Name",
"kdaa6ae2b": "Monitor Count",
"kdaff25a6": "Show Latest Value",
"kdb61adbb": "Hide Offline",
"kdbadcf43": "All Systems Operational",
"kdbe222b": "Api Key",
"kdc10ee1a": "Create a new workspace to cooperate with team members.",
"kdc15c5d": "Data",
"kdc1bf80e": "Url is required",
"kdc51b5db": "Websites",
"kdd44ac01": "Telemetry Name to Display",
"kdd55936a": "Resolver Port",
"kde315178": "Rename",
"kde37bc27": "Back to Admin",
"kdeba7706": "Devices",
"kdeecbfea": "Resolver Server",
@ -390,7 +359,6 @@
"kf246dd2e": "Not any workspace has been found, please create first",
"kf3b749ef": "Support Direct Chat / Group / Channel's Chat ID",
"kf55495e0": "Save",
"kf5c3b616": "Request Header",
"kf5c9520e": "No any status page yet, you can create a new one to show your service status to public.",
"kf6339d4f": "Verified",
"kf6582ba": "Workspace",
@ -406,7 +374,6 @@
"kf97b6f71": "Run this command in your linux machine",
"kf9877f28": "View Details",
"kf9965c19": "All content in this workspace will be destory and can not recover.",
"kf9a498c7": "Lighthouse report completed!",
"kfc98929b": "{{num}} days",
"kfd33c459": "Copy success!",
"kfdaf0bb3": "Last online: {{time}}",

View File

Before

Width:  |  Height:  |  Size: 267 B

After

Width:  |  Height:  |  Size: 267 B

View File

@ -19,14 +19,11 @@
"k17058821": "Rapports Lighthouse du site Web",
"k172a09c3": "Suggestions",
"k1777bbf2": "Manuel",
"k1940fd6": "Général",
"k1964b988": "Arrêter",
"k1bd89236": "exécuter le rapporteur avec",
"k1c33c293": "Paramètres",
"k1d8f92b4": "Tablette",
"k1da4ecc2": "Vous pouvez envoyer un message à ce canal avec :",
"k1eb5b3ed": "Aperçu",
"k1ee0c2ca": "Définissez l'URL du webhook sur <1></1>, et gardez cette fenêtre active. Une fois terminé, vous commencerez à recevoir des requêtes webhook ici.",
"k1f6dea0": "Nom du canal",
"k2099f2e0": "Échec de la connexion, veuillez vérifier votre nom d'utilisateur et votre mot de passe",
"k20edf271": "24h",
@ -56,15 +53,12 @@
"k2c84fe32": "Nombre d'événements de flux",
"k2cecf817": "Type",
"k2dad13e3": "Langue",
"k2db2c0c5": "Test Notify",
"k2e6dbf02": "À l'email",
"k2ea8a019": "Moniteur",
"k30b5f01b": "Espaces de travail",
"k30d33d71": "Signature du Webhook",
"k310fee": "30 derniers jours",
"k32344f64": "Effacer les données",
"k3260f019": "Déconnexion",
"k3404b72f": "Nouveau nom d'espace de travail",
"k340547f0": "Désolé, mais quelque chose s'est mal passé",
"k3471e956": "Répéter le nouveau mot de passe",
"k34981fea": "Docker est à la dérive en mer, incapable de trouver son chemin. Veuillez démarrer Docker pour revenir sur la bonne voie.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Rejoindre Discord",
"k3eaab921": "Liste de surveillance",
"k3f36e17e": "Suivre Twitter",
"k406089a4": "Action",
"k406e9ad8": "Confirmer",
"k41d3ce6c": "Événement désarchivé",
"k42347b91": "Nombre d'événements sur le site Web",
@ -100,8 +93,7 @@
"k44186b66": "Compte",
"k44cad477": "(Actuel)",
"k45f80a27": "Avancé",
"k4727e4db": "Expiré À",
"k477b7ee4": "Panne partielle du système",
"k4738284": "Vous pouvez envoyer n'importe quel message dans ce canal avec :",
"k47fe1f95": "Ajoutez ce code d'exemple à votre projet",
"k48186ce": "Retour à la page d'accueil",
"k4905ed7b": "AUCUN",
@ -115,7 +107,6 @@
"k4de48e75": "Nombre maximum de tentatives",
"k4e08cf58": "Afficher le numéro de détail",
"k4eea9393": "Profil",
"k4f182a7c": "Panne majeure du système",
"k4fc2b5b": "Image",
"k4fe1b4de": "Télémétrie",
"k505c2733": "Créer un rapport",
@ -132,12 +123,9 @@
"k58267a45": "Source",
"k58f90514": "Jeton de bot",
"k593cf342": "Êtes-vous sûr de vouloir supprimer ce moniteur ?",
"k5a782f4b": "Nombre de Sites Web",
"k5a839f71": "Disponibilité",
"k5b5be0d4": "Rôle actuel",
"k5c18db28": "Modifier les informations de la page d'état",
"k5d00536d": "Copié",
"k5d49d751": "La nouvelle clé API a été copiée dans votre presse-papiers !",
"k5eb87a8b": "Démarrer",
"k5ec0de4": "Pour la surveillance HTTPS, si une méthode de notification est assignée, des notifications seront envoyées à 1, 3, 7 et 14 jours avant l'expiration.",
"k5ecf04b0": "Vue",
@ -147,7 +135,6 @@
"k62e19375": "Dernière mise à jour : {{date}}",
"k6488f302": "Optionnel",
"k659b065": "Par exemple : https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Corps de la requête",
"k67c5a895": "Hier",
"k683be220": "Exécuter",
"k691b7170": "Arrêté",
@ -160,11 +147,9 @@
"k6e96fc3": "Informations sur le formulaire",
"k6ea11aff": "Obtenir !",
"k6f15bcc3": "Hôte",
"k71067412": "Optionnel, signature du webhook pour le webhook entrant",
"k721589c1": "Aujourd'hui",
"k7247683c": "Supprimer l'espace de travail",
"k7350bd93": "En même temps, nous pouvons également l'utiliser dans certains scénarios d'application côté client, comme la collecte de la fréquence d'utilisation du CLI, la collecte de l'installation d'applications auto-hébergées, etc.",
"k736f3e4c": "Copier en tant que",
"k75581e13": "Sous-titres",
"k75bfaaa6": "Ajoutez ce code dans le script de tête de votre site web",
"k763816ac": "Aperçu",
@ -172,17 +157,16 @@
"k78b1ef6a": "Entrer",
"k7927b824": "Êtes-vous sûr de vouloir effacer tous les nœuds hors ligne ?",
"k7a132ce8": "Désolé, mais cette page est introuvable",
"k7a15497a": "Temps réel",
"k7ac44a6e": "Clé de session",
"k7b74a43f": "visiteurs",
"k7b75e24c": "Intégration",
"k7b9aa48c": "Corps",
"k7cac602a": "Statut",
"k7d8cd81c": "Copier l'URL",
"k7e0360fd": "Aucun groupe n'a été créé, cliquez sur le bouton pour en créer un",
"k7e61b1af": "Sélectionner l'espace de travail",
"k7f01b47c": "Journal d'audit",
"k7f03a704": "N'oubliez pas de ne pas envoyer de données avec application/json",
"k7f29bae5": "Vue de la page",
"k8037cc6b": "Serveurs",
"k816ce026": "Télécharger",
"k819633bc": "Utiliser pour le stockage",
@ -192,7 +176,6 @@
"k84ce1618": "(24 heures)",
"k84e82947": "{{num}} événements effacés",
"k85344b23": "Charge",
"k85a116ee": "URL du Webhook",
"k85c5fd4c": "Aucun moniteur n'a été défini",
"k85db19da": "Pas encore de canal de flux. Utilisez la fonctionnalité de flux pour recevoir tous les événements du réseau ou de votre propre service.",
"k873c90e6": "Étiquette d'affichage",
@ -205,7 +188,6 @@
"k88d2647b": "Site Web",
"k89056082": "(30 jours)",
"k892f84b6": "Impossible d'obtenir les informations de l'utilisateur actuel",
"k895cafe1": "Optionnel, URL du webhook pour envoyer la charge utile de l'enquête",
"k899fd0cd": "Ports",
"k89d54f7a": "Compte des exécutions surveillées",
"k8a1deb63": "Membres",
@ -225,10 +207,7 @@
"k90b603b8": "Dupliquer",
"k90b668e5": "24 dernières heures",
"k93374bc9": "Supprimer le site Web",
"k93458b98": "Terrain de jeu",
"k951a939a": "Compte accepté par le site Web",
"k95f932a": "En attente d'une nouvelle requête du serveur distant",
"k97b02874": "Nombre de Pages",
"k98f433ee": "Télécharger le rapporteur de",
"k9991c290": "Communauté",
"k9a272ecf": "S'agit-il de vos serveurs ?",
@ -254,7 +233,6 @@
"ka6ee7455": "ID du site Web",
"ka71c12e1": "Les deux mots de passe ne sont pas cohérents",
"ka765ad32": "Notification",
"ka7d8617e": "Nombre de Canaux de Flux",
"ka7fe5937": "Lecture/écriture de disque",
"ka8e41156": "Rechercher et sauter rapidement",
"ka90bc019": "Désinstaller",
@ -276,7 +254,6 @@
"kb0e351e0": "Rafraîchi",
"kb114a2e8": "Obsolète",
"kb15a6374": "Vous pouvez configurer votre page de statut sur votre propre domaine, par exemple : status.example.com",
"kb2dded49": "Clé",
"kb320aac4": "Surveillé pendant {{dayNum}} jours",
"kb35cde91": "Recherche",
"kb35d71ed": "OU",
@ -284,7 +261,6 @@
"kb5673707": "7 derniers jours",
"kb659c1bc": "Expiration du cert.",
"kb6d350b6": "Canaux de flux",
"kb7bf8869": "Clés API",
"kb7fa344a": "Sélectionner le canal de flux à envoyer",
"kb8de8c50": "CCI",
"kbb31d3db": "Date de statistique",
@ -320,16 +296,13 @@
"kcc9c1bff": "Toutes les semaines",
"kccaa732a": "Pas de tirets consécutifs",
"kccb42483": "Mot de passe",
"kcd56f27b": "Dernière mise à jour",
"kcd643ef3": "Chargement...",
"kce77d0c1": "Fuseau horaire",
"kcff78587": "Dernière Utilisation À",
"kd005f7a8": "Tous les flux seront supprimés",
"kd031b383": "Vues",
"kd044d5d4": "Session",
"kd092de58": "Espace de travail actuel :",
"kd1f7e695": "Confirmer la déconnexion",
"kd211e2d4": "Page des versions",
"kd25f123a": "Statut inconnu",
"kd2a7ad83": "Modèle de flux",
"kd3262a4a": "Configuration",
"kd3396544": "Généralement, nous utiliserons une image vide d'un pixel de sorte qu'elle n'affecte pas l'utilisation normale de l'utilisateur.",
@ -338,18 +311,14 @@
"kd7279fa6": "Code",
"kd7985726": "{{num}} utilisateurs",
"kd92fa3e7": "Nom de l'hôte",
"kdaa6ae2b": "Nombre de Moniteurs",
"kdaff25a6": "Afficher la dernière valeur",
"kdb61adbb": "Masquer hors ligne",
"kdbadcf43": "Tous les systèmes opérationnels",
"kdbe222b": "Clé API",
"kdc10ee1a": "Créer un nouvel espace de travail pour coopérer avec les membres de l'équipe.",
"kdc15c5d": "Données",
"kdc1bf80e": "L'URL est requise",
"kdc51b5db": "Sites Web",
"kdd44ac01": "Nom de la télémétrie à afficher",
"kdd55936a": "Port de résolveur",
"kde315178": "Renommer",
"kde37bc27": "Retour à l'administrateur",
"kdeba7706": "Appareils",
"kdeecbfea": "Serveur de résolveur",
@ -390,7 +359,6 @@
"kf246dd2e": "Aucun espace de travail n'a été trouvé, veuillez d'abord en créer un",
"kf3b749ef": "Prend en charge le chat direct / groupe / ID de chat de canal",
"kf55495e0": "Sauvegarder",
"kf5c3b616": "En-tête de la requête",
"kf5c9520e": "Pas encore de page de statut, vous pouvez en créer une nouvelle pour afficher l'état de votre service au public.",
"kf6339d4f": "Vérifié",
"kf6582ba": "Espace de travail",
@ -406,7 +374,6 @@
"kf97b6f71": "Exécutez cette commande sur votre machine Linux",
"kf9877f28": "Voir les détails",
"kf9965c19": "Tout le contenu de cet espace de travail sera détruit et ne pourra pas être récupéré.",
"kf9a498c7": "Rapport Lighthouse terminé !",
"kfc98929b": "{{num}} jours",
"kfd33c459": "Copie réussie !",
"kfdaf0bb3": "Dernière connexion : {{time}}",

View File

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 450 B

View File

@ -19,14 +19,11 @@
"k17058821": "ウェブサイト ライトハウス レポート",
"k172a09c3": "提案",
"k1777bbf2": "マニュアル",
"k1940fd6": "一般",
"k1964b988": "停止",
"k1bd89236": "レポーターを実行する",
"k1c33c293": "設定",
"k1d8f92b4": "タブレット",
"k1da4ecc2": "このチャンネルにメッセージを送信できます:",
"k1eb5b3ed": "概要",
"k1ee0c2ca": "Webhook URLを<1></1>に設定し、このウィンドウをアクティブに保ってください。完了すると、ここでWebhookリクエストを受信し始めます。",
"k1f6dea0": "チャンネル名",
"k2099f2e0": "ログインに失敗しました。ユーザー名とパスワードを確認してください",
"k20edf271": "24時間",
@ -56,15 +53,12 @@
"k2c84fe32": "フィードイベント数",
"k2cecf817": "タイプ",
"k2dad13e3": "言語",
"k2db2c0c5": "テスト通知",
"k2e6dbf02": "メールアドレスへ",
"k2ea8a019": "モニター",
"k30b5f01b": "ワークスペース",
"k30d33d71": "Webhook署名",
"k310fee": "過去30日間",
"k32344f64": "データクリア",
"k3260f019": "ログアウト",
"k3404b72f": "新しいワークスペース名",
"k340547f0": "申し訳ありませんが、何か問題が発生しました",
"k3471e956": "新しいパスワードの再入力",
"k34981fea": "Dockerは海上で漂流しており、方向を見失っています。Dockerを起動して進路を修正してください。",
@ -91,7 +85,6 @@
"k3e8b13f8": "Discordに参加",
"k3eaab921": "モニターリスト",
"k3f36e17e": "Twitterをフォロー",
"k406089a4": "アクション",
"k406e9ad8": "確認",
"k41d3ce6c": "イベントがアーカイブ解除されました",
"k42347b91": "ウェブサイトイベント数",
@ -100,8 +93,7 @@
"k44186b66": "カウント",
"k44cad477": "(現在)",
"k45f80a27": "詳細",
"k4727e4db": "期限切れ",
"k477b7ee4": "部分的なシステム障害",
"k4738284": "次の方法でこのチャンネルにメッセージを送信できます:",
"k47fe1f95": "このサンプルコードをプロジェクトに追加してください",
"k48186ce": "ホームページに戻る",
"k4905ed7b": "なし",
@ -115,7 +107,6 @@
"k4de48e75": "最大リトライ回数",
"k4e08cf58": "詳細番号を表示",
"k4eea9393": "プロファイル",
"k4f182a7c": "重大なシステム障害",
"k4fc2b5b": "画像",
"k4fe1b4de": "テレメトリー",
"k505c2733": "レポートを作成",
@ -132,12 +123,9 @@
"k58267a45": "ソース",
"k58f90514": "ボットトークン",
"k593cf342": "このモニターを削除してもよろしいですか?",
"k5a782f4b": "ウェブサイト数",
"k5a839f71": "アップタイム",
"k5b5be0d4": "現在の役割",
"k5c18db28": "ステータスページ情報を変更",
"k5d00536d": "コピー済み",
"k5d49d751": "新しいAPIキーがクリップボードにコピーされました",
"k5eb87a8b": "開始",
"k5ec0de4": "HTTPSモニタリングの場合、通知方法が割り当てられている場合、有効期限の1、3、7、14日前に通知が送信されます。",
"k5ecf04b0": "ビュー",
@ -147,7 +135,6 @@
"k62e19375": "最終更新:{{date}}",
"k6488f302": "オプション",
"k659b065": "例https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "リクエストボディ",
"k67c5a895": "昨日",
"k683be220": "実行",
"k691b7170": "停止済み",
@ -160,11 +147,9 @@
"k6e96fc3": "フォーム情報",
"k6ea11aff": "取得!",
"k6f15bcc3": "ホスト",
"k71067412": "オプション、受信WebhookのWebhook署名",
"k721589c1": "今日",
"k7247683c": "ワークスペースを削除",
"k7350bd93": "同時に、CLIの使用頻度の収集、自己ホスト型アプリのインストールの収集など、クライアントサイドのアプリケーションシナリオでも使用することができます。",
"k736f3e4c": "コピーとして",
"k75581e13": "CC",
"k75bfaaa6": "このコードをウェブサイトのヘッドスクリプトに追加してください",
"k763816ac": "プレビュー",
@ -172,17 +157,16 @@
"k78b1ef6a": "入力",
"k7927b824": "すべてのオフラインノードをクリアしてもよろしいですか?",
"k7a132ce8": "申し訳ありませんが、このページは見つかりません",
"k7a15497a": "リアルタイム",
"k7ac44a6e": "セッションキー",
"k7b74a43f": "訪問者",
"k7b75e24c": "統合",
"k7b9aa48c": "ボディ",
"k7cac602a": "ステータス",
"k7d8cd81c": "URLをコピー",
"k7e0360fd": "グループが作成されていません。ボタンをクリックして作成してください",
"k7e61b1af": "ワークスペースを選択",
"k7f01b47c": "監査ログ",
"k7f03a704": "application/json でデータを送信しないことを忘れないでください",
"k7f29bae5": "ページビュー",
"k8037cc6b": "サーバー",
"k816ce026": "ダウンロード",
"k819633bc": "ストレージ用",
@ -192,7 +176,6 @@
"k84ce1618": "24時間",
"k84e82947": "{{num}} イベントがクリアされました",
"k85344b23": "ロード",
"k85a116ee": "Webhook URL",
"k85c5fd4c": "まだモニターが設定されていません",
"k85db19da": "まだフィードチャンネルがありません。ネットワークや自分のサービスからのすべてのイベントを受信するには、フィード機能を使用してください。",
"k873c90e6": "表示ラベル",
@ -205,7 +188,6 @@
"k88d2647b": "ウェブサイト",
"k89056082": "30日間",
"k892f84b6": "現在のユーザー情報を取得できません",
"k895cafe1": "オプション、調査ペイロードを送信するためのWebhook URL",
"k899fd0cd": "ポート",
"k89d54f7a": "実行カウントの監視",
"k8a1deb63": "メンバー",
@ -225,10 +207,7 @@
"k90b603b8": "重複",
"k90b668e5": "過去24時間",
"k93374bc9": "ウェブサイトを削除",
"k93458b98": "プレイグラウンド",
"k951a939a": "ウェブサイト承認カウント",
"k95f932a": "現在、リモートサーバーからの新しいリクエストを待機中です",
"k97b02874": "ページ数",
"k98f433ee": "からレポーターをダウンロード",
"k9991c290": "コミュニティ",
"k9a272ecf": "これはあなたのサーバーですか?",
@ -254,7 +233,6 @@
"ka6ee7455": "ウェブサイトID",
"ka71c12e1": "2つのパスワードが一致しません",
"ka765ad32": "通知",
"ka7d8617e": "フィードチャンネル数",
"ka7fe5937": "ディスク読み取り/書き込み",
"ka8e41156": "検索して素早く移動",
"ka90bc019": "アンインストール",
@ -276,7 +254,6 @@
"kb0e351e0": "更新されました",
"kb114a2e8": "非推奨",
"kb15a6374": "自分のドメインでステータスページを設定できます。たとえば、status.example.com",
"kb2dded49": "キー",
"kb320aac4": "{{dayNum}}日間監視",
"kb35cde91": "検索",
"kb35d71ed": "または",
@ -284,7 +261,6 @@
"kb5673707": "過去7日間",
"kb659c1bc": "証明書の有効期限",
"kb6d350b6": "フィードチャンネル",
"kb7bf8869": "APIキー",
"kb7fa344a": "送信するフィードチャンネルを選択",
"kb8de8c50": "BCC",
"kbb31d3db": "統計日",
@ -320,16 +296,13 @@
"kcc9c1bff": "毎週",
"kccaa732a": "連続ダッシュなし",
"kccb42483": "パスワード",
"kcd56f27b": "最終更新",
"kcd643ef3": "読み込み中...",
"kce77d0c1": "タイムゾーン",
"kcff78587": "最終使用日時",
"kd005f7a8": "すべてのフィードが削除されます",
"kd031b383": "ビュー",
"kd044d5d4": "セッション",
"kd092de58": "現在のワークスペース:",
"kd1f7e695": "ログアウトを確認",
"kd211e2d4": "リリースページ",
"kd25f123a": "ステータス不明",
"kd2a7ad83": "フィードテンプレート",
"kd3262a4a": "設定",
"kd3396544": "一般的に、ユーザーの通常の使用に影響を与えないように、1ピクセルの空白画像を使用します。",
@ -338,18 +311,14 @@
"kd7279fa6": "コード",
"kd7985726": "{{num}}人のユーザー",
"kd92fa3e7": "ホスト名",
"kdaa6ae2b": "モニター数",
"kdaff25a6": "最新値を表示",
"kdb61adbb": "オフラインを隠す",
"kdbadcf43": "すべてのシステムが稼働中",
"kdbe222b": "APIキー",
"kdc10ee1a": "チームメンバーと協力するために新しいワークスペースを作成します。",
"kdc15c5d": "データ",
"kdc1bf80e": "URLは必須です",
"kdc51b5db": "ウェブサイト",
"kdd44ac01": "表示するテレメトリー名",
"kdd55936a": "リゾルバーポート",
"kde315178": "名前を変更",
"kde37bc27": "管理者に戻る",
"kdeba7706": "デバイス",
"kdeecbfea": "リゾルバーサーバー",
@ -390,7 +359,6 @@
"kf246dd2e": "ワークスペースが見つかりません。最初に作成してください。",
"kf3b749ef": "ダイレクトチャット/グループ/チャネルのチャットIDをサポート",
"kf55495e0": "保存",
"kf5c3b616": "リクエストヘッダー",
"kf5c9520e": "まだステータスページがありません。新しいステータスページを作成して、サービスのステータスを公開することができます。",
"kf6339d4f": "確認済み",
"kf6582ba": "ワークスペース",
@ -406,7 +374,6 @@
"kf97b6f71": "Linuxマシンでこのコマンドを実行してください",
"kf9877f28": "詳細を見る",
"kf9965c19": "このワークスペース内のすべてのコンテンツは破壊され、復元できません。",
"kf9a498c7": "Lighthouseレポートが完了しました",
"kfc98929b": "{{num}}日",
"kfd33c459": "コピーに成功しました!",
"kfdaf0bb3": "最後のオンライン:{{time}}",

View File

Before

Width:  |  Height:  |  Size: 259 B

After

Width:  |  Height:  |  Size: 259 B

View File

@ -19,14 +19,11 @@
"k17058821": "Raporty Lighthouse Strony",
"k172a09c3": "Sugestie",
"k1777bbf2": "Instrukcja obsługi",
"k1940fd6": "Ogólne",
"k1964b988": "Zatrzymaj",
"k1bd89236": "uruchom raportera z",
"k1c33c293": "Ustawienia",
"k1d8f92b4": "Tablet",
"k1da4ecc2": "Możesz wysłać wiadomość do tego kanału za pomocą:",
"k1eb5b3ed": "Przegląd",
"k1ee0c2ca": "Ustaw adres URL webhooka na <1></1> i utrzymuj to okno aktywne. Po zakończeniu zaczniesz otrzymywać żądania webhooka tutaj.",
"k1f6dea0": "Nazwa kanału",
"k2099f2e0": "Logowanie nie powiodło się, sprawdź swoją nazwę użytkownika i hasło",
"k20edf271": "24h",
@ -56,15 +53,12 @@
"k2c84fe32": "Liczba zdarzeń w kanale",
"k2cecf817": "Typ",
"k2dad13e3": "Język",
"k2db2c0c5": "Test Powiadomienie",
"k2e6dbf02": "Do e-maila",
"k2ea8a019": "Monitorować",
"k30b5f01b": "Obszary robocze",
"k30d33d71": "Podpis Webhooka",
"k310fee": "Ostatnie 30 dni",
"k32344f64": "Wyczyść dane",
"k3260f019": "Wyloguj",
"k3404b72f": "Nowa nazwa przestrzeni roboczej",
"k340547f0": "Przepraszamy, ale coś poszło nie tak",
"k3471e956": "Powtórz nowe hasło",
"k34981fea": "Docker dryfuje po morzu, nie mogąc znaleźć drogi. Uruchom Docker, aby wrócić na właściwy kurs.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Dołącz do Discorda",
"k3eaab921": "Lista monitorów",
"k3f36e17e": "Śledź na Twitterze",
"k406089a4": "Akcja",
"k406e9ad8": "Potwierdź",
"k41d3ce6c": "Wydarzenie odarchiwizowane",
"k42347b91": "Liczba zdarzeń na stronie internetowej",
@ -100,8 +93,7 @@
"k44186b66": "Liczba",
"k44cad477": "(Obecny)",
"k45f80a27": "Zaawansowane",
"k4727e4db": "Wygasło",
"k477b7ee4": "Częściowa awaria systemu",
"k4738284": "Możesz wysłać dowolną wiadomość do tego kanału za pomocą:",
"k47fe1f95": "Dodaj ten przykładowy kod do swojego projektu",
"k48186ce": "Powrót do strony głównej",
"k4905ed7b": "BRAK",
@ -115,7 +107,6 @@
"k4de48e75": "Maksymalna liczba prób",
"k4e08cf58": "Pokaż liczbę szczegółów",
"k4eea9393": "Profil",
"k4f182a7c": "Poważna awaria systemu",
"k4fc2b5b": "Obraz",
"k4fe1b4de": "Telemetria",
"k505c2733": "Utwórz Raport",
@ -132,12 +123,9 @@
"k58267a45": "Źródło",
"k58f90514": "Token Bota",
"k593cf342": "Czy na pewno chcesz usunąć ten monitor?",
"k5a782f4b": "Liczba stron internetowych",
"k5a839f71": "Czas działania",
"k5b5be0d4": "Aktualna Rola",
"k5c18db28": "Zmień informacje na stronie statusu",
"k5d00536d": "Skopiowane",
"k5d49d751": "Nowy klucz API został skopiowany do schowka!",
"k5eb87a8b": "Wznów",
"k5ec0de4": "Dla monitorowania HTTPS, jeśli przypisana jest jakakolwiek metoda powiadamiania, powiadomienia zostaną wysłane 1, 3, 7 i 14 dni przed wygaśnięciem.",
"k5ecf04b0": "Widok",
@ -147,7 +135,6 @@
"k62e19375": "Ostatnia aktualizacja: {{date}}",
"k6488f302": "Opcjonalne",
"k659b065": "Na przykład: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Treść żądania",
"k67c5a895": "Wczoraj",
"k683be220": "Uruchom",
"k691b7170": "Zatrzymany",
@ -160,11 +147,9 @@
"k6e96fc3": "Informacje o formularzu",
"k6ea11aff": "OK!",
"k6f15bcc3": "Host",
"k71067412": "Opcjonalne, podpis webhooka dla przychodzącego webhooka",
"k721589c1": "Dziś",
"k7247683c": "Usuń przestrzeń roboczą",
"k7350bd93": "W tym samym czasie możemy go również użyć w niektórych scenariuszach aplikacji po stronie klienta, takich jak zbieranie częstotliwości używania wiersza poleceń, takie jak zbieranie instalacji aplikacji selfhosted itp.",
"k736f3e4c": "Kopiuj jako",
"k75581e13": "DW",
"k75bfaaa6": "Dodaj ten kod do sekcji head na swojej stronie internetowej",
"k763816ac": "Podgląd",
@ -172,17 +157,16 @@
"k78b1ef6a": "Wprowadź",
"k7927b824": "Czy na pewno chcesz wyczyścić wszystkie wyłączone węzły?",
"k7a132ce8": "Przepraszamy, ale ta strona nie została znaleziona",
"k7a15497a": "Na żywo",
"k7ac44a6e": "Klucz sesji",
"k7b74a43f": "odwiedzający",
"k7b75e24c": "Integracja",
"k7b9aa48c": "Treść",
"k7cac602a": "Status",
"k7d8cd81c": "Kopiuj URL",
"k7e0360fd": "Nie utworzono żadnej grupy, kliknij przycisk, aby utworzyć jedną",
"k7e61b1af": "Wybierz przestrzeń roboczą",
"k7f01b47c": "Dziennik audytu",
"k7f03a704": "Pamiętaj, aby nie wysyłać danych za pomocą application/json",
"k7f29bae5": "wyświetlenia strony",
"k8037cc6b": "Serwery",
"k816ce026": "Pobierz",
"k819633bc": "Użyj do przechowywania",
@ -192,7 +176,6 @@
"k84ce1618": "(24 godziny)",
"k84e82947": "{{num}} zdarzeń usuniętych",
"k85344b23": "Obciążenie",
"k85a116ee": "Adres URL Webhooka",
"k85c5fd4c": "Nie ustawiono żadnego monitora",
"k85db19da": "Brak kanałów informacyjnych. Użyj funkcji kanałów, aby otrzymywać wszystkie zdarzenia z sieci lub własnej usługi.",
"k873c90e6": "Etykieta wyświetlania",
@ -205,7 +188,6 @@
"k88d2647b": "Strona internetowa",
"k89056082": "(30 dni)",
"k892f84b6": "Nie można uzyskać informacji o bieżącym użytkowniku",
"k895cafe1": "Opcjonalne, adres url webhooka do wysyłania ładunku ankiety",
"k899fd0cd": "Porty",
"k89d54f7a": "Liczba wykonań monitora",
"k8a1deb63": "Członkowie",
@ -225,10 +207,7 @@
"k90b603b8": "Duplikat",
"k90b668e5": "Ostatnie 24 godziny",
"k93374bc9": "Usuń stronę internetową",
"k93458b98": "Plac zabaw",
"k951a939a": "Liczba zaakceptowanych stron internetowych",
"k95f932a": "Obecnie czekam na nowe żądanie z zdalnego serwera",
"k97b02874": "Liczba stron",
"k98f433ee": "Pobierz reporter z",
"k9991c290": "Społeczność",
"k9a272ecf": "Czy to twoje serwery?",
@ -254,7 +233,6 @@
"ka6ee7455": "ID strony internetowej",
"ka71c12e1": "Dwa hasła nie są zgodne",
"ka765ad32": "Powiadomienie",
"ka7d8617e": "Liczba kanałów feed",
"ka7fe5937": "Odczyt/zapis dysku",
"ka8e41156": "Wyszukiwanie i szybkie przeskakiwanie",
"ka90bc019": "Odinstaluj",
@ -276,7 +254,6 @@
"kb0e351e0": "Odświeżone",
"kb114a2e8": "Przestarzałe",
"kb15a6374": "Możesz skonfigurować swoją stronę statusu pod własną domeną, na przykład: status.example.com",
"kb2dded49": "Klucz",
"kb320aac4": "Monitorowane przez {{dayNum}} dni",
"kb35cde91": "Szukaj",
"kb35d71ed": "LUB",
@ -284,7 +261,6 @@
"kb5673707": "Ostatnie 7 dni",
"kb659c1bc": "Wygaśnięcie certyfikatu",
"kb6d350b6": "Kanały feedu",
"kb7bf8869": "Klucze API",
"kb7fa344a": "Wybierz kanał feedu do wysłania",
"kb8de8c50": "DWU",
"kbb31d3db": "Data statystyk",
@ -308,7 +284,7 @@
"kc5f82d53": "Na przykład: pushdeer://pushKey",
"kc6888ac4": "Automatyczny",
"kc6cac621": "(Brak)",
"kc6dc3c38": "Komputer stacjonarny",
"kc6dc3c38": "Desktop",
"kc70d69ad": "Odpowiedź",
"kc9b446d1": "Zakończono uruchamianie",
"kcacbfde1": "Utwórz teraz",
@ -320,16 +296,13 @@
"kcc9c1bff": "Każdy tydzień",
"kccaa732a": "Brak kolejnych myślników",
"kccb42483": "Hasło",
"kcd56f27b": "Ostatnia aktualizacja",
"kcd643ef3": "Ładowanie...",
"kce77d0c1": "Strefa czasowa",
"kcff78587": "Ostatnie użycie",
"kd005f7a8": "Wszystkie kanały informacyjne zostaną usunięte",
"kd031b383": "Odsłony",
"kd044d5d4": "sesja",
"kd092de58": "Aktualna przestrzeń robocza:",
"kd1f7e695": "Potwierdź wylogowanie",
"kd211e2d4": "Strona wydań",
"kd25f123a": "Status nieznany",
"kd2a7ad83": "Szablon feedu",
"kd3262a4a": "Konfiguracja",
"kd3396544": "Zazwyczaj użyjemy pustego obrazu o rozmiarze jednego piksela, aby nie wpływał na normalne użytkowanie użytkownika.",
@ -338,18 +311,14 @@
"kd7279fa6": "Kod",
"kd7985726": "{{num}} użytkowników",
"kd92fa3e7": "Nazwa hosta",
"kdaa6ae2b": "Liczba monitorów",
"kdaff25a6": "Pokaż najnowszą wartość",
"kdb61adbb": "Ukryj wyłączone",
"kdbadcf43": "Wszystkie systemy działają",
"kdbe222b": "Klucz API",
"kdc10ee1a": "Utwórz nową przestrzeń roboczą, aby współpracować z członkami zespołu.",
"kdc15c5d": "Dane",
"kdc1bf80e": "Url jest wymagany",
"kdc51b5db": "Strony internetowe",
"kdd44ac01": "Nazwa telemetrii do wyświetlenia",
"kdd55936a": "Port resolvera",
"kde315178": "Zmień nazwę",
"kde37bc27": "Powrót do panelu administratora",
"kdeba7706": "Urządzenia",
"kdeecbfea": "Serwer resolvera",
@ -390,7 +359,6 @@
"kf246dd2e": "Nie znaleziono żadnej przestrzeni roboczej, proszę najpierw utworzyć",
"kf3b749ef": "Wsparcie dla czatu bezpośredniego / grupy / czatu kanału",
"kf55495e0": "Zapisz",
"kf5c3b616": "Nagłówek żądania",
"kf5c9520e": "Brak stron statusu, możesz utworzyć nową, aby pokazać stan swojej usługi publicznie.",
"kf6339d4f": "Zweryfikowane",
"kf6582ba": "Przestrzeń robocza",
@ -406,7 +374,6 @@
"kf97b6f71": "Uruchom to polecenie na swojej maszynie z systemem Linux",
"kf9877f28": "Pokaż szczegóły",
"kf9965c19": "Cała zawartość w tej przestrzeni roboczej zostanie zniszczona i nie można jej odzyskać.",
"kf9a498c7": "Raport Lighthouse zakończony!",
"kfc98929b": "{{num}} dni",
"kfd33c459": "Kopiowanie powiodło się!",
"kfdaf0bb3": "O na pewno chcesz usunąć wszystkie zdarzenia dla tego monitora?",

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -19,14 +19,11 @@
"k17058821": "Relatórios do Website Lighthouse",
"k172a09c3": "Sugestões",
"k1777bbf2": "Manual",
"k1940fd6": "Geral",
"k1964b988": "Parar",
"k1bd89236": "correr repórter com",
"k1c33c293": "Definições",
"k1d8f92b4": "Tablet",
"k1da4ecc2": "Você pode enviar uma mensagem para este canal com:",
"k1eb5b3ed": "Visão geral",
"k1ee0c2ca": "Defina a URL do webhook para <1></1> e mantenha esta janela ativa. Uma vez feito, você começará a receber solicitações de webhook aqui.",
"k1f6dea0": "Nome do Canal",
"k2099f2e0": "Falha no login, verifique seu nome de usuário e senha",
"k20edf271": "24 horas",
@ -56,15 +53,12 @@
"k2c84fe32": "Contagem de eventos de feed",
"k2cecf817": "Tipo",
"k2dad13e3": "Idioma",
"k2db2c0c5": "Notificação de Teste",
"k2e6dbf02": "Para o e-mail",
"k2ea8a019": "Monitorar",
"k30b5f01b": "Áreas de trabalho",
"k30d33d71": "Assinatura do Webhook",
"k310fee": "Últimos 30 dias",
"k32344f64": "Limpar dados",
"k3260f019": "Terminar sessão",
"k3404b72f": "Novo Nome do Espaço de Trabalho",
"k340547f0": "Desculpe, mas algo correu mal",
"k3471e956": "Repetir nova palavra-passe",
"k34981fea": "O Docker está à deriva no mar, incapaz de encontrar seu caminho. Por favor, inicie o Docker para voltar ao curso.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Aderir ao Discord",
"k3eaab921": "Lista de Monitoramento",
"k3f36e17e": "Seguir o Twitter",
"k406089a4": "Ação",
"k406e9ad8": "Confirmar",
"k41d3ce6c": "Evento desarquivado",
"k42347b91": "Contagem de eventos do sítio Web",
@ -100,8 +93,7 @@
"k44186b66": "Contar",
"k44cad477": "(Atual)",
"k45f80a27": "Avançado",
"k4727e4db": "Expirado Em",
"k477b7ee4": "Interrupção Parcial do Sistema",
"k4738284": "Você pode enviar qualquer mensagem para este canal com:",
"k47fe1f95": "Adicione este código de exemplo ao seu projeto",
"k48186ce": "Voltar à página inicial",
"k4905ed7b": "NENHUM",
@ -115,7 +107,6 @@
"k4de48e75": "Máximo de tentativas",
"k4e08cf58": "Mostrar número de pormenor",
"k4eea9393": "Perfil",
"k4f182a7c": "Interrupção Maior do Sistema",
"k4fc2b5b": "Imagem",
"k4fe1b4de": "Telemetria",
"k505c2733": "Criar Relatório",
@ -132,12 +123,9 @@
"k58267a45": "Tipo de Letra",
"k58f90514": "Token de Bot",
"k593cf342": "De certeza que eliminou este monitor?",
"k5a782f4b": "Contagem de Sites",
"k5a839f71": "Tempo de atividade",
"k5b5be0d4": "Função Atual",
"k5c18db28": "Modificar Informações da Página de Status",
"k5d00536d": "Copiado",
"k5d49d751": "Nova chave de API foi copiada para sua área de transferência!",
"k5eb87a8b": "Início",
"k5ec0de4": "Para monitoramento HTTPS, se algum método de notificação estiver atribuído, notificações serão enviadas com 1, 3, 7 e 14 dias antes do vencimento.",
"k5ecf04b0": "Ver",
@ -147,7 +135,6 @@
"k62e19375": "Última atualização em: {{date}}",
"k6488f302": "Opcional",
"k659b065": "Por exemplo: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Corpo da Solicitação",
"k67c5a895": "Ontem",
"k683be220": "Correr",
"k691b7170": "Parado",
@ -160,11 +147,9 @@
"k6e96fc3": "Informações do formulário",
"k6ea11aff": "Obter!",
"k6f15bcc3": "Anfitrião",
"k71067412": "Opcional, Assinatura do Webhook para Webhook de Entrada",
"k721589c1": "Hoje",
"k7247683c": "Excluir Espaço de Trabalho",
"k7350bd93": "Ao mesmo tempo, também podemos utilizá-lo em alguns cenários de aplicações do lado do cliente, como a recolha da frequência de utilização do cli, como a recolha da instalação de aplicações auto-hospedadas, etc.",
"k736f3e4c": "Copiar como",
"k75581e13": "CC",
"k75bfaaa6": "Adicionar este código ao script principal do seu sítio Web",
"k763816ac": "Pré-visualização",
@ -172,17 +157,16 @@
"k78b1ef6a": "Entrar",
"k7927b824": "Tem a certeza de que pretende limpar todos os nós offline?",
"k7a132ce8": "Desculpe, mas esta página não foi encontrada",
"k7a15497a": "Em Tempo Real",
"k7ac44a6e": "Chave de sessão",
"k7b74a43f": "visitantes",
"k7b75e24c": "Integração",
"k7b9aa48c": "Corpo",
"k7cac602a": "Estado",
"k7d8cd81c": "Copiar URL",
"k7e0360fd": "Nenhum grupo foi criado, clique no botão para criar um",
"k7e61b1af": "Selecionar Espaço de Trabalho",
"k7f01b47c": "Registo de auditoria",
"k7f03a704": "Não se esqueça de não enviar dados com application/json",
"k7f29bae5": "visualização de página",
"k8037cc6b": "Servidores",
"k816ce026": "Baixar",
"k819633bc": "Usar para armazenamento",
@ -192,7 +176,6 @@
"k84ce1618": "(24 horas)",
"k84e82947": "{{num}} eventos limpos",
"k85344b23": "Carregar",
"k85a116ee": "URL do Webhook",
"k85c5fd4c": "Não foi definido qualquer monitor",
"k85db19da": "Ainda não há nenhum canal de feed. Use o recurso de feed para receber todos os eventos da rede ou do seu próprio serviço.",
"k873c90e6": "Etiqueta de Exibição",
@ -205,7 +188,6 @@
"k88d2647b": "Sítio Web",
"k89056082": "(30 dias)",
"k892f84b6": "Não é possível obter as informações do usuário atual",
"k895cafe1": "Opcional, url do webhook para enviar carga de pesquisa",
"k899fd0cd": "Portos",
"k89d54f7a": "Contagem de Execução do Monitor",
"k8a1deb63": "Membros",
@ -225,10 +207,7 @@
"k90b603b8": "Duplicar",
"k90b668e5": "Últimas 24 horas",
"k93374bc9": "Eliminar sítio Web",
"k93458b98": "Playground",
"k951a939a": "Contagem de sites aceites",
"k95f932a": "Aguardando atualmente uma nova solicitação do servidor remoto",
"k97b02874": "Contagem de Páginas",
"k98f433ee": "Descarregar repórter de",
"k9991c290": "Comunidade",
"k9a272ecf": "Estes são os vossos servidores?",
@ -254,7 +233,6 @@
"ka6ee7455": "ID do sítio Web",
"ka71c12e1": "As duas palavras-passe não são consistentes",
"ka765ad32": "Notificação",
"ka7d8617e": "Contagem de Canais de Feed",
"ka7fe5937": "Leitura/escrita de disco",
"ka8e41156": "Pesquisa e salto rápido",
"ka90bc019": "Desinstalar",
@ -276,7 +254,6 @@
"kb0e351e0": "Atualizado",
"kb114a2e8": "Obsoleto",
"kb15a6374": "Você pode configurar sua página de status em seu próprio domínio, por exemplo: status.example.com",
"kb2dded49": "Chave",
"kb320aac4": "Monitorizado durante {{dayNum}} dias",
"kb35cde91": "Pesquisar",
"kb35d71ed": "OU",
@ -284,7 +261,6 @@
"kb5673707": "Últimos 7 dias",
"kb659c1bc": "Exp. do certificado",
"kb6d350b6": "Canais de Feed",
"kb7bf8869": "Chaves de API",
"kb7fa344a": "Selecione o Canal de Feed para enviar",
"kb8de8c50": "CCO",
"kbb31d3db": "Data da estatística",
@ -320,16 +296,13 @@
"kcc9c1bff": "Toda semana",
"kccaa732a": "Sem traços consecutivos",
"kccb42483": "Palavra-passe",
"kcd56f27b": "Última atualização",
"kcd643ef3": "Carregando...",
"kce77d0c1": "Fuso Horário",
"kcff78587": "Último Uso Em",
"kd005f7a8": "Todos os feeds serão removidos",
"kd031b383": "Vistas",
"kd044d5d4": "sessão",
"kd092de58": "Espaço de Trabalho Atual:",
"kd1f7e695": "Confirmar para terminar a sessão",
"kd211e2d4": "Página de lançamentos",
"kd25f123a": "Status Desconhecido",
"kd2a7ad83": "Modelo de Feed",
"kd3262a4a": "Configuração",
"kd3396544": "Geralmente, utilizamos uma imagem em branco de um pixel para que não afecte a utilização normal do utilizador.",
@ -338,18 +311,14 @@
"kd7279fa6": "Código",
"kd7985726": "{{num}} utilizadores",
"kd92fa3e7": "Nome do anfitrião",
"kdaa6ae2b": "Contagem de Monitores",
"kdaff25a6": "Mostrar valor mais recente",
"kdb61adbb": "Ocultar offline",
"kdbadcf43": "Todos os Sistemas Operacionais",
"kdbe222b": "Chave de API",
"kdc10ee1a": "Crie um novo espaço de trabalho para cooperar com os membros da equipe.",
"kdc15c5d": "Dados",
"kdc1bf80e": "Url é obrigatório",
"kdc51b5db": "Sites",
"kdd44ac01": "Nome de telemetria a apresentar",
"kdd55936a": "Porta do resolvedor",
"kde315178": "Renomear",
"kde37bc27": "Voltar ao Administrador",
"kdeba7706": "Dispositivos",
"kdeecbfea": "Servidor de resolução",
@ -390,7 +359,6 @@
"kf246dd2e": "Nenhum espaço de trabalho foi encontrado, por favor crie primeiro",
"kf3b749ef": "ID de Chat Direto do Suporte / Grupo / Canal",
"kf55495e0": "Guardar",
"kf5c3b616": "Cabeçalho da Solicitação",
"kf5c9520e": "Ainda não há nenhuma página de status, você pode criar uma nova para mostrar o status do seu serviço ao público.",
"kf6339d4f": "Verificado",
"kf6582ba": "Espaço de Trabalho",
@ -406,7 +374,6 @@
"kf97b6f71": "Executar este comando na sua máquina linux",
"kf9877f28": "Ver detalhes",
"kf9965c19": "Todo o conteúdo neste espaço de trabalho será destruído e não poderá ser recuperado.",
"kf9a498c7": "Relatório do Lighthouse concluído!",
"kfc98929b": "{{num}} dias",
"kfd33c459": "Cópia bem sucedida!",
"kfdaf0bb3": "Última vez online: {{tempo}}",

View File

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 268 B

View File

@ -19,14 +19,11 @@
"k17058821": "Отчеты Lighthouse для веб-сайтов",
"k172a09c3": "Предложения",
"k1777bbf2": "Вручную",
"k1940fd6": "Общее",
"k1964b988": "Остановить",
"k1bd89236": "запустить репортер с",
"k1c33c293": "Настройки",
"k1d8f92b4": "Планшет",
"k1da4ecc2": "Вы можете отправить сообщение в этот канал с помощью:",
"k1eb5b3ed": "Обзор",
"k1ee0c2ca": "Установите URL вебхука на <1></1> и оставьте это окно активным. После завершения вы начнете получать запросы вебхука здесь.",
"k1f6dea0": "Название канала",
"k2099f2e0": "Ошибка входа, проверьте имя пользователя и пароль",
"k20edf271": "24ч",
@ -56,15 +53,12 @@
"k2c84fe32": "Количество событий ленты",
"k2cecf817": "Тип",
"k2dad13e3": "Язык",
"k2db2c0c5": "Тестовое уведомление",
"k2e6dbf02": "На Email",
"k2ea8a019": "Монитор",
"k30b5f01b": "Рабочие области",
"k30d33d71": "Подпись вебхука",
"k310fee": "Последние 30 дней",
"k32344f64": "Очистить данные",
"k3260f019": "Выйти",
"k3404b72f": "Новое имя рабочего пространства",
"k340547f0": "Извините, но что-то пошло не так",
"k3471e956": "Повтор нового пароля",
"k34981fea": "Docker дрейфует в море, не может найти свой путь. Пожалуйста, запустите Docker, чтобы вернуться на правильный курс.",
@ -91,7 +85,6 @@
"k3e8b13f8": "Присоединяйтесь к Discord",
"k3eaab921": "Список мониторинга",
"k3f36e17e": "Подписаться на Twitter",
"k406089a4": "Действие",
"k406e9ad8": "Подтвердить",
"k41d3ce6c": "Событие восстановлено",
"k42347b91": "Количество событий на сайте",
@ -100,8 +93,7 @@
"k44186b66": "Количество",
"k44cad477": "(Текущий)",
"k45f80a27": "Расширенный",
"k4727e4db": "Истекает",
"k477b7ee4": "Частичный сбой системы",
"k4738284": "Вы можете отправить любое сообщение в этот канал с помощью:",
"k47fe1f95": "Добавьте этот пример кода в ваш проект",
"k48186ce": "Вернуться на главную страницу",
"k4905ed7b": "НИКАКОЙ",
@ -115,7 +107,6 @@
"k4de48e75": "Макс. попыток",
"k4e08cf58": "Показать подробное количество",
"k4eea9393": "Профиль",
"k4f182a7c": "Крупный сбой системы",
"k4fc2b5b": "Изображение",
"k4fe1b4de": "Телеметрия",
"k505c2733": "Создать отчет",
@ -132,12 +123,9 @@
"k58267a45": "Источник",
"k58f90514": "Токен бота",
"k593cf342": "Вы уверены, что хотите удалить этот монитор?",
"k5a782f4b": "Количество сайтов",
"k5a839f71": "Время работы",
"k5b5be0d4": "Текущая роль",
"k5c18db28": "Изменить информацию на странице статуса",
"k5d00536d": "Скопировано",
"k5d49d751": "Новый API-ключ скопирован в буфер обмена!",
"k5eb87a8b": "Старт",
"k5ec0de4": "Для мониторинга HTTPS, если назначен любой метод уведомления, уведомления будут отправлены за 1, 3, 7 и 14 дней до истечения срока действия.",
"k5ecf04b0": "Просмотр",
@ -147,7 +135,6 @@
"k62e19375": "Последнее обновление: {{date}}",
"k6488f302": "Необязательно",
"k659b065": "Например: https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "Тело запроса",
"k67c5a895": "Вчера",
"k683be220": "Запустить",
"k691b7170": "Остановлено",
@ -160,11 +147,9 @@
"k6e96fc3": "Информация формы",
"k6ea11aff": "Получить!",
"k6f15bcc3": "Хост",
"k71067412": "Необязательно, подпись вебхука для входящего вебхука",
"k721589c1": "Сегодня",
"k7247683c": "Удалить рабочее пространство",
"k7350bd93": "В то же время, мы также можем использовать это в некоторых сценариях клиентского приложения, таких как сбор частоты использования cli, сбор установок самостоятельно размещенных приложений и так далее.",
"k736f3e4c": "Копировать как",
"k75581e13": "Копия",
"k75bfaaa6": "Добавьте этот код в скрипт заголовка вашего веб-сайта",
"k763816ac": "Предварительный просмотр",
@ -172,17 +157,16 @@
"k78b1ef6a": "Ввод",
"k7927b824": "Вы уверены, что хотите очистить все офлайн узлы?",
"k7a132ce8": "Извините, но эта страница не найдена",
"k7a15497a": "В реальном времени",
"k7ac44a6e": "Ключ сессии",
"k7b74a43f": "посетители",
"k7b75e24c": "Интеграция",
"k7b9aa48c": "Тело",
"k7cac602a": "Статус",
"k7d8cd81c": "Копировать URL",
"k7e0360fd": "Не создано ни одной группы, нажмите кнопку, чтобы создать одну",
"k7e61b1af": "Выбрать рабочее пространство",
"k7f01b47c": "Журнал аудита",
"k7f03a704": "Не забудьте не отправлять данные с application/json",
"k7f29bae5": "Просмотр страницы",
"k8037cc6b": "Серверы",
"k816ce026": "Скачать",
"k819633bc": "Использовать для хранения",
@ -192,7 +176,6 @@
"k84ce1618": "(24 часа)",
"k84e82947": "{{num}} события очищены",
"k85344b23": "Нагрузка",
"k85a116ee": "URL вебхука",
"k85c5fd4c": "Мониторы еще не настроены",
"k85db19da": "Пока нет ни одного канала. Используйте функцию канала для получения всех событий из сети или вашей собственной службы.",
"k873c90e6": "Метка отображения",
@ -205,7 +188,6 @@
"k88d2647b": "Веб-сайт",
"k89056082": "(30 дней)",
"k892f84b6": "Не удается получить информацию о текущем пользователе",
"k895cafe1": "Необязательно, URL вебхука для отправки полезной нагрузки опроса",
"k899fd0cd": "Порты",
"k89d54f7a": "Количество выполнений мониторинга",
"k8a1deb63": "Участники",
@ -225,10 +207,7 @@
"k90b603b8": "Дублировать",
"k90b668e5": "Последние 24 часа",
"k93374bc9": "Удалить веб-сайт",
"k93458b98": "Площадка",
"k951a939a": "Количество принятых сайтом",
"k95f932a": "В настоящее время ожидает нового запроса от удаленного сервера",
"k97b02874": "Количество страниц",
"k98f433ee": "Скачать репортер с",
"k9991c290": "Сообщество",
"k9a272ecf": "Это ваши серверы?",
@ -254,7 +233,6 @@
"ka6ee7455": "ID веб-сайта",
"ka71c12e1": "Два пароля не совпадают",
"ka765ad32": "Уведомления",
"ka7d8617e": "Количество каналов ленты",
"ka7fe5937": "Чтение/запись на диск",
"ka8e41156": "Поиск и быстрый переход",
"ka90bc019": "Удалить",
@ -276,7 +254,6 @@
"kb0e351e0": "Обновлено",
"kb114a2e8": "Устаревший",
"kb15a6374": "Вы можете настроить свою страницу статуса на своем собственном домене, например: status.example.com",
"kb2dded49": "Ключ",
"kb320aac4": "Мониторинг в течение {{dayNum}} дней",
"kb35cde91": "Поиск",
"kb35d71ed": "ИЛИ",
@ -284,7 +261,6 @@
"kb5673707": "Последние 7 дней",
"kb659c1bc": "Истечение серт.",
"kb6d350b6": "Каналы обратной связи",
"kb7bf8869": "API-ключи",
"kb7fa344a": "Выберите канал обратной связи для отправки",
"kb8de8c50": "Скрытая копия",
"kbb31d3db": "Дата статистики",
@ -320,16 +296,13 @@
"kcc9c1bff": "Каждую неделю",
"kccaa732a": "Без последовательных тире",
"kccb42483": "Пароль",
"kcd56f27b": "Последнее обновление",
"kcd643ef3": "Загрузка...",
"kce77d0c1": "Часовой пояс",
"kcff78587": "Последнее использование",
"kd005f7a8": "Все ленты будут удалены",
"kd031b383": "Просмотры",
"kd044d5d4": "Сессия",
"kd092de58": "Текущее рабочее пространство:",
"kd1f7e695": "Подтвердить выход",
"kd211e2d4": "Страница релизов",
"kd25f123a": "Статус неизвестен",
"kd2a7ad83": "Шаблон обратной связи",
"kd3262a4a": "Настройка",
"kd3396544": "Обычно мы будем использовать однопиксельное пустое изображение, так что это не повлияет на нормальное использование пользователя.",
@ -338,18 +311,14 @@
"kd7279fa6": "Код",
"kd7985726": "{{num}} пользователей",
"kd92fa3e7": "Имя хоста",
"kdaa6ae2b": "Количество мониторов",
"kdaff25a6": "Показать последнее значение",
"kdb61adbb": "Скрыть офлайн",
"kdbadcf43": "Все системы работают",
"kdbe222b": "API-ключ",
"kdc10ee1a": "Создайте новое рабочее пространство для сотрудничества с членами команды.",
"kdc15c5d": "Данные",
"kdc1bf80e": "URL обязателен",
"kdc51b5db": "Веб-сайты",
"kdd44ac01": "Отображаемое имя телеметрии",
"kdd55936a": "Порт разрешителя",
"kde315178": "Переименовать",
"kde37bc27": "Вернуться к администратору",
"kdeba7706": "Устройства",
"kdeecbfea": "Сервер разрешителя",
@ -390,7 +359,6 @@
"kf246dd2e": "Рабочее пространство не найдено, пожалуйста, создайте его сначала",
"kf3b749ef": "Поддержка прямого чата / группы / ID чата канала",
"kf55495e0": "Сохранить",
"kf5c3b616": "Заголовок запроса",
"kf5c9520e": "Пока нет страницы состояния, вы можете создать новую, чтобы показать статус вашего сервиса общественности.",
"kf6339d4f": "Подтверждено",
"kf6582ba": "Рабочее пространство",
@ -406,7 +374,6 @@
"kf97b6f71": "Запустите эту команду на вашем Linux-машине",
"kf9877f28": "Посмотреть детали",
"kf9965c19": "Всё содержимое в этом рабочем пространстве будет уничтожено и не может быть восстановлено.",
"kf9a498c7": "Отчет Lighthouse завершен!",
"kfc98929b": "{{num}} дней",
"kfd33c459": "Копирование успешно!",
"kfdaf0bb3": "Последний онлайн: {{time}}",

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -19,14 +19,11 @@
"k17058821": "网站灯塔报告",
"k172a09c3": "建议",
"k1777bbf2": "手动",
"k1940fd6": "常规",
"k1964b988": "停止",
"k1bd89236": "运行报告器",
"k1c33c293": "设置",
"k1d8f92b4": "平板电脑",
"k1da4ecc2": "您可以通过以下方式向此频道发送消息:",
"k1eb5b3ed": "概览",
"k1ee0c2ca": "将 webhook URL 设置为 <1></1>,并保持此窗口处于活动状态。完成后,您将开始在此接收 webhook 请求。",
"k1f6dea0": "频道名称",
"k2099f2e0": "登录失败,请检查您的用户名和密码",
"k20edf271": "24小时",
@ -56,15 +53,12 @@
"k2c84fe32": "事件计数",
"k2cecf817": "类型",
"k2dad13e3": "语言",
"k2db2c0c5": "测试通知",
"k2e6dbf02": "发邮件到",
"k2ea8a019": "监控器",
"k30b5f01b": "工作区",
"k30d33d71": "Webhook 签名",
"k310fee": "最近30天",
"k32344f64": "清除数据",
"k3260f019": "登出",
"k3404b72f": "新工作区名称",
"k340547f0": "抱歉,出了点问题",
"k3471e956": "重复新密码",
"k34981fea": "Docker在海上漂流无法找到方向。请启动Docker以重新导航。",
@ -91,7 +85,6 @@
"k3e8b13f8": "加入 Discord",
"k3eaab921": "监控列表",
"k3f36e17e": "关注 Twitter",
"k406089a4": "操作",
"k406e9ad8": "确认",
"k41d3ce6c": "事件已取消归档",
"k42347b91": "网站事件计数",
@ -100,8 +93,7 @@
"k44186b66": "计数",
"k44cad477": "(当前)",
"k45f80a27": "高级",
"k4727e4db": "到期时间",
"k477b7ee4": "部分系统故障",
"k4738284": "你可以通过以下方式向此频道发送任何消息:",
"k47fe1f95": "将此示例代码添加到您的项目中",
"k48186ce": "返回首页",
"k4905ed7b": "无",
@ -115,7 +107,6 @@
"k4de48e75": "最大重试次数",
"k4e08cf58": "显示详细数字",
"k4eea9393": "个人资料",
"k4f182a7c": "重大系统故障",
"k4fc2b5b": "图片",
"k4fe1b4de": "遥测",
"k505c2733": "创建报告",
@ -132,12 +123,9 @@
"k58267a45": "源",
"k58f90514": "机器人令牌",
"k593cf342": "您确定要删除这个监控器吗?",
"k5a782f4b": "网站数量",
"k5a839f71": "正常运行时间",
"k5b5be0d4": "当前角色",
"k5c18db28": "修改状态页面信息",
"k5d00536d": "已复制",
"k5d49d751": "新的 API 密钥已复制到您的剪贴板!",
"k5eb87a8b": "开始",
"k5ec0de4": "对于 HTTPS 监控,如果分配了任何通知方法,则将在到期前 1、3、7 和 14 天发送通知。",
"k5ecf04b0": "查看",
@ -147,7 +135,6 @@
"k62e19375": "最后更新时间:{{date}}",
"k6488f302": "可选",
"k659b065": "示例https://open.feishu.cn/open-apis/bot/v2/hook/00000000-0000-0000-0000-000000000000",
"k678e2f90": "请求体",
"k67c5a895": "昨天",
"k683be220": "运行",
"k691b7170": "已停止",
@ -160,11 +147,9 @@
"k6e96fc3": "表单信息",
"k6ea11aff": "获取!",
"k6f15bcc3": "主机",
"k71067412": "可选,传入 webhook 的 webhook 签名",
"k721589c1": "今天",
"k7247683c": "删除工作区",
"k7350bd93": "同时我们也可以在一些客户端应用场景中使用它比如收集cli使用频率比如收集自托管应用的安装情况等。",
"k736f3e4c": "复制为",
"k75581e13": "抄送",
"k75bfaaa6": "将此代码添加到您的网站头部脚本中",
"k763816ac": "预览",
@ -172,17 +157,16 @@
"k78b1ef6a": "输入",
"k7927b824": "您确定要清除所有离线节点吗?",
"k7a132ce8": "抱歉,找不到此页面",
"k7a15497a": "实时",
"k7ac44a6e": "会话密钥",
"k7b74a43f": "访客",
"k7b75e24c": "集成",
"k7b9aa48c": "正文",
"k7cac602a": "状态",
"k7d8cd81c": "复制 URL",
"k7e0360fd": "尚未创建任何组,点击按钮创建一个",
"k7e61b1af": "选择工作区",
"k7f01b47c": "审计日志",
"k7f03a704": "记得不要使用 application/json 发送数据",
"k7f29bae5": "页面查看",
"k8037cc6b": "服务器",
"k816ce026": "下载",
"k819633bc": "用于存储",
@ -192,7 +176,6 @@
"k84ce1618": "24小时",
"k84e82947": "{{num}} 事件已清除",
"k85344b23": "负载",
"k85a116ee": "Webhook Url",
"k85c5fd4c": "还没有设置任何监控器",
"k85db19da": "还没有任何订阅频道。使用订阅功能接收来自网络或您自己服务的所有事件。",
"k873c90e6": "显示标签",
@ -205,7 +188,6 @@
"k88d2647b": "网站",
"k89056082": "30天",
"k892f84b6": "无法获取当前用户信息",
"k895cafe1": "可选,发送调查有效负载的 webhook url",
"k899fd0cd": "端口",
"k89d54f7a": "监控执行计数",
"k8a1deb63": "成员",
@ -225,10 +207,7 @@
"k90b603b8": "重复",
"k90b668e5": "最近24小时",
"k93374bc9": "删除网站",
"k93458b98": "游乐场",
"k951a939a": "网站接受计数",
"k95f932a": "当前正在等待来自远程服务器的新请求",
"k97b02874": "页面数量",
"k98f433ee": "从这里下载报告器",
"k9991c290": "社区",
"k9a272ecf": "这是您的服务器吗?",
@ -254,7 +233,6 @@
"ka6ee7455": "网站ID",
"ka71c12e1": "两次密码不一致",
"ka765ad32": "通知",
"ka7d8617e": "Feed 渠道数量",
"ka7fe5937": "磁盘读/写",
"ka8e41156": "搜索和快速跳转",
"ka90bc019": "卸载",
@ -276,7 +254,6 @@
"kb0e351e0": "已刷新",
"kb114a2e8": "已弃用",
"kb15a6374": "您可以在自己的域名中配置您的状态页面例如status.example.com",
"kb2dded49": "密钥",
"kb320aac4": "已监控{{dayNum}}天",
"kb35cde91": "搜索",
"kb35d71ed": "或",
@ -284,7 +261,6 @@
"kb5673707": "最近7天",
"kb659c1bc": "证书到期",
"kb6d350b6": "馈送频道",
"kb7bf8869": "API 密钥",
"kb7fa344a": "选择要发送的馈送频道",
"kb8de8c50": "密送",
"kbb31d3db": "统计日期",
@ -320,16 +296,13 @@
"kcc9c1bff": "每周",
"kccaa732a": "无连续破折号",
"kccb42483": "密码",
"kcd56f27b": "最后更新",
"kcd643ef3": "加载中...",
"kce77d0c1": "时区",
"kcff78587": "最后使用时间",
"kd005f7a8": "所有订阅将被删除",
"kd031b383": "视图",
"kd044d5d4": "会话",
"kd092de58": "当前工作区:",
"kd1f7e695": "确认注销",
"kd211e2d4": "发布页面",
"kd25f123a": "状态未知",
"kd2a7ad83": "馈送模板",
"kd3262a4a": "配置",
"kd3396544": "通常,我们会使用一个 1x1 像素的空白图片,这样不会影响用户的正常使用。",
@ -338,18 +311,14 @@
"kd7279fa6": "代码",
"kd7985726": "{{num}}个用户",
"kd92fa3e7": "主机名",
"kdaa6ae2b": "监控数量",
"kdaff25a6": "显示最新值",
"kdb61adbb": "隐藏离线",
"kdbadcf43": "所有系统正常运行",
"kdbe222b": "API 密钥",
"kdc10ee1a": "创建一个新的工作区以与团队成员合作。",
"kdc15c5d": "数据",
"kdc1bf80e": "网址是必需的",
"kdc51b5db": "网站",
"kdd44ac01": "显示的遥测名称",
"kdd55936a": "解析器端口",
"kde315178": "重命名",
"kde37bc27": "返回管理员",
"kdeba7706": "设备",
"kdeecbfea": "解析器服务器",
@ -390,7 +359,6 @@
"kf246dd2e": "未找到任何工作区,请先创建",
"kf3b749ef": "支持直接聊天/群组/频道的聊天ID",
"kf55495e0": "保存",
"kf5c3b616": "请求头",
"kf5c9520e": "还没有任何状态页面,您可以创建一个新的状态页面向公众展示您的服务状态。",
"kf6339d4f": "已验证",
"kf6582ba": "工作区",
@ -406,7 +374,6 @@
"kf97b6f71": "在您的Linux机器上运行此命令",
"kf9877f28": "查看详情",
"kf9965c19": "此工作区中的所有内容将被销毁,无法恢复。",
"kf9a498c7": "灯塔报告已完成!",
"kfc98929b": "{{num}}天",
"kfd33c459": "复制成功!",
"kfdaf0bb3": "最后在线时间:{{time}}",

View File

@ -34,9 +34,7 @@ import { Route as SettingsWorkspaceImport } from './routes/settings/workspace'
import { Route as SettingsUsageImport } from './routes/settings/usage'
import { Route as SettingsProfileImport } from './routes/settings/profile'
import { Route as SettingsNotificationsImport } from './routes/settings/notifications'
import { Route as SettingsBillingImport } from './routes/settings/billing'
import { Route as SettingsAuditLogImport } from './routes/settings/auditLog'
import { Route as SettingsApiKeyImport } from './routes/settings/apiKey'
import { Route as PageAddImport } from './routes/page/add'
import { Route as PageSlugImport } from './routes/page/$slug'
import { Route as MonitorAddImport } from './routes/monitor/add'
@ -168,21 +166,11 @@ const SettingsNotificationsRoute = SettingsNotificationsImport.update({
getParentRoute: () => SettingsRoute,
} as any)
const SettingsBillingRoute = SettingsBillingImport.update({
path: '/billing',
getParentRoute: () => SettingsRoute,
} as any)
const SettingsAuditLogRoute = SettingsAuditLogImport.update({
path: '/auditLog',
getParentRoute: () => SettingsRoute,
} as any)
const SettingsApiKeyRoute = SettingsApiKeyImport.update({
path: '/apiKey',
getParentRoute: () => SettingsRoute,
} as any)
const PageAddRoute = PageAddImport.update({
path: '/add',
getParentRoute: () => PageRoute,
@ -324,18 +312,10 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof PageAddImport
parentRoute: typeof PageImport
}
'/settings/apiKey': {
preLoaderRoute: typeof SettingsApiKeyImport
parentRoute: typeof SettingsImport
}
'/settings/auditLog': {
preLoaderRoute: typeof SettingsAuditLogImport
parentRoute: typeof SettingsImport
}
'/settings/billing': {
preLoaderRoute: typeof SettingsBillingImport
parentRoute: typeof SettingsImport
}
'/settings/notifications': {
preLoaderRoute: typeof SettingsNotificationsImport
parentRoute: typeof SettingsImport
@ -431,9 +411,7 @@ export const routeTree = rootRoute.addChildren([
RegisterRoute,
ServerRoute,
SettingsRoute.addChildren([
SettingsApiKeyRoute,
SettingsAuditLogRoute,
SettingsBillingRoute,
SettingsNotificationsRoute,
SettingsProfileRoute,
SettingsUsageRoute,

View File

@ -114,12 +114,7 @@ function PageComponent() {
{info?.id && (
<DialogWrapper
title={t('Integration')}
content={
<FeedIntegration
feedId={info.id}
webhookSignature={info.webhookSignature}
/>
}
content={<FeedIntegration feedId={info.id} />}
>
<Button variant="default" size="icon" Icon={LuWebhook} />
</DialogWrapper>
@ -199,12 +194,7 @@ function PageComponent() {
)}
renderEmpty={() => (
<div className="w-full overflow-hidden p-4">
{!isInitialLoading && (
<FeedApiGuide
channelId={channelId}
webhookSignature={info?.webhookSignature}
/>
)}
{!isInitialLoading && <FeedApiGuide channelId={channelId} />}
</div>
)}
/>

View File

@ -8,12 +8,6 @@ import { useState } from 'react';
import { EditableText } from '@/components/EditableText';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { WebhookPlayground } from '@/components/WebhookPlayground';
import React from 'react';
import { defaultErrorHandler, defaultSuccessHandler, trpc } from '@/api/trpc';
import { Button } from '@/components/ui/button';
import { useEvent } from '@/hooks/useEvent';
import { useCurrentWorkspaceId } from '@/store/user';
import { Separator } from '@/components/ui/separator';
export const Route = createFileRoute('/playground')({
beforeLoad: () => {
@ -54,22 +48,18 @@ function PageComponent() {
return (
<div className="h-full w-full p-4">
<Tabs defaultValue="billing" className="flex h-full flex-col">
<Tabs defaultValue="current" className="flex h-full flex-col">
<div>
<TabsList>
<TabsTrigger value="billing">Billing</TabsTrigger>
<TabsTrigger value="webhook">Webhook</TabsTrigger>
<TabsTrigger value="misc">Misc</TabsTrigger>
<TabsTrigger value="current">Current</TabsTrigger>
<TabsTrigger value="history">History</TabsTrigger>
</TabsList>
</div>
<TabsContent value="billing">
<BillingPlayground />
</TabsContent>
<TabsContent value="webhook" className="flex-1 overflow-hidden">
<TabsContent value="current" className="flex-1 overflow-hidden">
<WebhookPlayground />
</TabsContent>
<TabsContent value="misc">
<TabsContent value="history">
<div>
<EditableText
defaultValue="fooooooooo"
@ -83,114 +73,3 @@ function PageComponent() {
</div>
);
}
export const BillingPlayground: React.FC = React.memo(() => {
const checkoutMutation = trpc.billing.checkout.useMutation({
onError: defaultErrorHandler,
});
const changePlanMutation = trpc.billing.changePlan.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const cancelSubscriptionMutation =
trpc.billing.cancelSubscription.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const workspaceId = useCurrentWorkspaceId();
const { data, refetch, isInitialLoading, isLoading } =
trpc.billing.currentSubscription.useQuery({
workspaceId,
});
const handleCheckoutSubscribe = useEvent(async (tier: 'pro' | 'team') => {
const { url } = await checkoutMutation.mutateAsync({
workspaceId,
tier,
redirectUrl: location.href,
});
location.href = url;
});
const handleChangeSubscribe = useEvent(
async (tier: 'free' | 'pro' | 'team') => {
await changePlanMutation.mutateAsync({
workspaceId,
tier,
});
refetch();
}
);
const plan = data ? (
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('free')}
>
Change plan to Free
</Button>
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('pro')}
>
Change plan to Pro
</Button>
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('team')}
>
Change plan to Team
</Button>
</div>
<div>
<Button
loading={cancelSubscriptionMutation.isLoading}
onClick={() =>
cancelSubscriptionMutation.mutateAsync({
workspaceId,
})
}
>
Cancel
</Button>
</div>
</div>
) : (
<div className="flex gap-2">
<Button
loading={checkoutMutation.isLoading}
onClick={() => handleCheckoutSubscribe('pro')}
>
Upgrade to Pro
</Button>
<Button
loading={checkoutMutation.isLoading}
onClick={() => handleCheckoutSubscribe('team')}
>
Upgrade to Team
</Button>
</div>
);
return (
<div className="flex flex-col gap-2">
<div>
<div>Current: {JSON.stringify(data)}</div>
<Button loading={isLoading} onClick={() => refetch()}>
Refresh
</Button>
</div>
<Separator className="my-2" />
{isInitialLoading === false && plan}
</div>
);
});
BillingPlayground.displayName = 'BillingPlayground';

View File

@ -2,7 +2,6 @@ import { CommonHeader } from '@/components/CommonHeader';
import { CommonList } from '@/components/CommonList';
import { CommonWrapper } from '@/components/CommonWrapper';
import { Layout } from '@/components/layout';
import { useGlobalConfig } from '@/hooks/useConfig';
import { routeAuthBeforeLoad } from '@/utils/route';
import { useTranslation } from '@i18next-toolkit/react';
import {
@ -10,7 +9,6 @@ import {
useNavigate,
useRouterState,
} from '@tanstack/react-router';
import { compact } from 'lodash-es';
import { useEffect } from 'react';
export const Route = createFileRoute('/settings')({
@ -24,9 +22,8 @@ function PageComponent() {
const pathname = useRouterState({
select: (state) => state.location.pathname,
});
const { enableBilling } = useGlobalConfig();
const items = compact([
const items = [
{
id: 'profile',
title: t('Profile'),
@ -42,11 +39,6 @@ function PageComponent() {
title: t('Workspace'),
href: '/settings/workspace',
},
{
id: 'apiKey',
title: t('Api Key'),
href: '/settings/apiKey',
},
{
id: 'auditLog',
title: t('Audit Log'),
@ -57,12 +49,7 @@ function PageComponent() {
title: t('Usage'),
href: '/settings/usage',
},
enableBilling && {
id: 'billing',
title: t('Billing'),
href: '/settings/billing',
},
]);
];
useEffect(() => {
if (pathname === Route.fullPath) {

View File

@ -1,157 +0,0 @@
import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { CommonWrapper } from '@/components/CommonWrapper';
import { ScrollArea } from '@/components/ui/scroll-area';
import { CommonHeader } from '@/components/CommonHeader';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { AppRouterOutput, defaultErrorHandler, trpc } from '@/api/trpc';
import { createColumnHelper, DataTable } from '@/components/DataTable';
import { useMemo } from 'react';
import { Button } from '@/components/ui/button';
import { useEvent } from '@/hooks/useEvent';
import dayjs from 'dayjs';
import { LuPlus, LuTrash } from 'react-icons/lu';
import copy from 'copy-to-clipboard';
import { toast } from 'sonner';
import { CopyableText } from '@/components/CopyableText';
import { AlertConfirm } from '@/components/AlertConfirm';
import { formatNumber } from '@/utils/common';
export const Route = createFileRoute('/settings/apiKey')({
beforeLoad: routeAuthBeforeLoad,
component: PageComponent,
});
type ApiKeyInfo = AppRouterOutput['user']['allApiKeys'][number];
const columnHelper = createColumnHelper<ApiKeyInfo>();
function PageComponent() {
const { t } = useTranslation();
const { data: apiKeys = [], refetch: refetchApiKeys } =
trpc.user.allApiKeys.useQuery();
const generateApiKeyMutation = trpc.user.generateApiKey.useMutation({
onError: defaultErrorHandler,
});
const deleteApiKeyMutation = trpc.user.deleteApiKey.useMutation({
onError: defaultErrorHandler,
});
const columns = useMemo(() => {
return [
columnHelper.accessor('apiKey', {
header: t('Key'),
size: 300,
cell: (props) => {
return (
<CopyableText text={props.getValue()}>
{props.getValue().slice(0, 20)}...
</CopyableText>
);
},
}),
columnHelper.accessor('usage', {
header: t('Usage'),
size: 80,
cell: (props) => {
return (
<div className="text-right">
{formatNumber(Number(props.getValue()))}
</div>
);
},
}),
columnHelper.accessor('createdAt', {
header: t('Created At'),
size: 130,
cell: (props) => {
const date = props.getValue();
return (
<span>
{date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : '-'}
</span>
);
},
}),
columnHelper.accessor('updatedAt', {
header: t('Last Use At'),
size: 130,
cell: (props) => {
const date = props.getValue();
return (
<span>
{date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : '-'}
</span>
);
},
}),
columnHelper.accessor('expiredAt', {
header: t('Expired At'),
size: 130,
cell: (props) => {
const date = props.getValue();
return (
<span>
{date ? dayjs(date).format('YYYY-MM-DD HH:mm:ss') : '-'}
</span>
);
},
}),
columnHelper.display({
id: 'action',
header: t('Action'),
size: 130,
cell: (props) => {
return (
<div>
<AlertConfirm
onConfirm={async () => {
await deleteApiKeyMutation.mutateAsync({
apiKey: props.row.original.apiKey,
});
refetchApiKeys();
}}
>
<Button variant="outline" size="icon" Icon={LuTrash} />
</AlertConfirm>
</div>
);
},
}),
];
}, [t]);
const handleGenerateApiKey = useEvent(async () => {
const apiKey = await generateApiKeyMutation.mutateAsync();
copy(apiKey);
toast.success(t('New api key has been copied into your clipboard!'));
refetchApiKeys();
});
return (
<CommonWrapper header={<CommonHeader title={t('Api Keys')} />}>
<ScrollArea className="h-full overflow-hidden p-4">
<div className="flex flex-col gap-4">
<Card>
<CardHeader className="text-lg font-bold">
<div className="flex items-center justify-between gap-2">
<div>{t('Api Keys')}</div>
<Button
Icon={LuPlus}
size="icon"
variant="outline"
onClick={handleGenerateApiKey}
/>
</div>
</CardHeader>
<CardContent>
<DataTable columns={columns} data={apiKeys} />
</CardContent>
</Card>
</div>
</ScrollArea>
</CommonWrapper>
);
}

View File

@ -2,13 +2,11 @@ import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { CommonWrapper } from '@/components/CommonWrapper';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Empty, List } from 'antd';
import { useMemo, useRef } from 'react';
import {
defaultErrorHandler,
defaultSuccessHandler,
trpc,
} from '../../api/trpc';
import { useCurrentWorkspaceId, useHasAdminPermission } from '../../store/user';
import { trpc } from '../../api/trpc';
import { useCurrentWorkspaceId } from '../../store/user';
import { CommonHeader } from '@/components/CommonHeader';
import { last } from 'lodash-es';
import { useVirtualizer } from '@tanstack/react-virtual';
@ -16,9 +14,6 @@ import { useWatch } from '@/hooks/useWatch';
import dayjs from 'dayjs';
import { ColorTag } from '@/components/ColorTag';
import { SimpleVirtualList } from '@/components/SimpleVirtualList';
import { Button } from '@/components/ui/button';
import { LuTrash2 } from 'react-icons/lu';
import { AlertConfirm } from '@/components/AlertConfirm';
export const Route = createFileRoute('/settings/auditLog')({
beforeLoad: routeAuthBeforeLoad,
@ -29,9 +24,8 @@ function PageComponent() {
const { t } = useTranslation();
const workspaceId = useCurrentWorkspaceId();
const parentRef = useRef<HTMLDivElement>(null);
const hasAdminPermission = useHasAdminPermission();
const { data, hasNextPage, fetchNextPage, isFetchingNextPage, refetch } =
const { data, hasNextPage, fetchNextPage, isFetchingNextPage } =
trpc.auditLog.fetchByCursor.useInfiniteQuery(
{
workspaceId,
@ -41,11 +35,6 @@ function PageComponent() {
}
);
const clearMutation = trpc.auditLog.clear.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const allData = useMemo(() => {
if (!data) {
return [];
@ -80,27 +69,7 @@ function PageComponent() {
});
return (
<CommonWrapper
header={
<CommonHeader
title={t('Audit Log')}
actions={
<>
{hasAdminPermission && (
<AlertConfirm
onConfirm={() => {
clearMutation.mutateAsync({ workspaceId });
refetch();
}}
>
<Button variant="outline" size="icon" Icon={LuTrash2} />
</AlertConfirm>
)}
</>
}
/>
}
>
<CommonWrapper header={<CommonHeader title={t('Audit Log')} />}>
<div className="h-full overflow-hidden p-4">
<SimpleVirtualList
allData={allData}

View File

@ -1,124 +0,0 @@
import { routeAuthBeforeLoad } from '@/utils/route';
import { createFileRoute } from '@tanstack/react-router';
import { useTranslation } from '@i18next-toolkit/react';
import { CommonWrapper } from '@/components/CommonWrapper';
import { ScrollArea } from '@/components/ui/scroll-area';
import { useMemo } from 'react';
import {
defaultErrorHandler,
defaultSuccessHandler,
trpc,
} from '../../api/trpc';
import { useCurrentWorkspace, useCurrentWorkspaceId } from '../../store/user';
import { CommonHeader } from '@/components/CommonHeader';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import dayjs from 'dayjs';
import { formatNumber } from '@/utils/common';
import { UsageCard } from '@/components/UsageCard';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { useEvent } from '@/hooks/useEvent';
import { SubscriptionSelection } from '@/components/billing/SubscriptionSelection';
export const Route = createFileRoute('/settings/billing')({
beforeLoad: routeAuthBeforeLoad,
component: PageComponent,
});
function PageComponent() {
const workspaceId = useCurrentWorkspaceId();
const { t } = useTranslation();
const { data: currentTier } = trpc.billing.currentTier.useQuery({
workspaceId,
});
const checkoutMutation = trpc.billing.checkout.useMutation({
onError: defaultErrorHandler,
});
const changePlanMutation = trpc.billing.changePlan.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const cancelSubscriptionMutation =
trpc.billing.cancelSubscription.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const { data, refetch, isInitialLoading, isLoading } =
trpc.billing.currentSubscription.useQuery({
workspaceId,
});
const handleChangeSubscribe = useEvent(
async (tier: 'free' | 'pro' | 'team') => {
await changePlanMutation.mutateAsync({
workspaceId,
tier,
});
refetch();
}
);
const plan = data ? (
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('free')}
>
Change plan to Free
</Button>
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('pro')}
>
Change plan to Pro
</Button>
<Button
loading={changePlanMutation.isLoading}
onClick={() => handleChangeSubscribe('team')}
>
Change plan to Team
</Button>
</div>
<div>
<Button
loading={cancelSubscriptionMutation.isLoading}
onClick={() =>
cancelSubscriptionMutation.mutateAsync({
workspaceId,
})
}
>
Cancel
</Button>
</div>
</div>
) : (
<div className="flex gap-2">
<SubscriptionSelection tier={currentTier} />
</div>
);
return (
<CommonWrapper header={<CommonHeader title={t('Billing')} />}>
<ScrollArea className="h-full overflow-hidden p-4">
<div className="flex flex-col gap-2">
<div>
<div>Current: {JSON.stringify(data)}</div>
<Button loading={isLoading} onClick={() => refetch()}>
Refresh
</Button>
</div>
<Separator className="my-2" />
{isInitialLoading === false && plan}
</div>
</ScrollArea>
</CommonWrapper>
);
}

View File

@ -10,7 +10,6 @@ import { CommonHeader } from '@/components/CommonHeader';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import dayjs from 'dayjs';
import { formatNumber } from '@/utils/common';
import { UsageCard } from '@/components/UsageCard';
export const Route = createFileRoute('/settings/usage')({
beforeLoad: routeAuthBeforeLoad,
@ -25,20 +24,12 @@ function PageComponent() {
[]
);
const { data: serviceCountData } = trpc.workspace.getServiceCount.useQuery({
workspaceId,
});
const { data: billingUsageData } = trpc.billing.usage.useQuery({
const { data } = trpc.billing.usage.useQuery({
workspaceId,
startAt: startDate.valueOf(),
endAt: endDate.valueOf(),
});
const { data: limit } = trpc.billing.limit.useQuery({
workspaceId,
});
return (
<CommonWrapper header={<CommonHeader title={t('Usage')} />}>
<ScrollArea className="h-full overflow-hidden p-4">
@ -54,61 +45,50 @@ function PageComponent() {
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 gap-2 sm:grid-cols-3">
<UsageCard
title={t('Website Count')}
current={serviceCountData?.website ?? 0}
limit={limit?.maxWebsiteCount}
/>
<Card className="flex-1">
<CardHeader className="text-muted-foreground">
{t('Website Accepted Count')}
</CardHeader>
<CardContent>
{formatNumber(data?.websiteAcceptedCount ?? 0)}
</CardContent>
</Card>
<UsageCard
title={t('Monitor Count')}
current={serviceCountData?.monitor ?? 0}
/>
<Card className="flex-1">
<CardHeader className="text-muted-foreground">
{t('Website Event Count')}
</CardHeader>
<CardContent>
{formatNumber(data?.websiteEventCount ?? 0)}
</CardContent>
</Card>
<UsageCard
title={t('Survey Count')}
current={serviceCountData?.survey ?? 0}
/>
<Card className="flex-1">
<CardHeader className="text-muted-foreground">
{t('Monitor Execution Count')}
</CardHeader>
<CardContent>
{formatNumber(data?.monitorExecutionCount ?? 0)}
</CardContent>
</Card>
<UsageCard
title={t('Page Count')}
current={serviceCountData?.page ?? 0}
/>
<Card className="flex-1">
<CardHeader className="text-muted-foreground">
{t('Survey Count')}
</CardHeader>
<CardContent>
{formatNumber(data?.surveyCount ?? 0)}
</CardContent>
</Card>
<UsageCard
title={t('Feed Channel Count')}
current={serviceCountData?.feed ?? 0}
limit={limit?.maxFeedChannelCount}
/>
<UsageCard
title={t('Website Accepted Count')}
current={billingUsageData?.websiteAcceptedCount ?? 0}
/>
<UsageCard
title={t('Website Event Count')}
current={billingUsageData?.websiteEventCount ?? 0}
limit={limit?.maxWebsiteEventCount}
/>
<UsageCard
title={t('Monitor Execution Count')}
current={billingUsageData?.monitorExecutionCount ?? 0}
limit={limit?.maxMonitorExecutionCount}
/>
<UsageCard
title={t('Survey Count')}
current={billingUsageData?.surveyCount ?? 0}
limit={limit?.maxSurveyCount}
/>
<UsageCard
title={t('Feed Event Count')}
current={billingUsageData?.feedEventCount ?? 0}
limit={limit?.maxFeedEventCount}
/>
<Card className="flex-1">
<CardHeader className="text-muted-foreground">
{t('Feed Event Count')}
</CardHeader>
<CardContent>
{formatNumber(data?.feedEventCount ?? 0)}
</CardContent>
</Card>
</div>
</CardContent>
</Card>

View File

@ -38,22 +38,13 @@ import {
} from '@/components/ui/form';
import { useForm } from 'react-hook-form';
import { Button } from '@/components/ui/button';
import { useEvent, useEventWithLoading } from '@/hooks/useEvent';
import { useEventWithLoading } from '@/hooks/useEvent';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { AlertConfirm } from '@/components/AlertConfirm';
import { ROLES } from '@tianji/shared';
import { cn } from '@/utils/style';
import { Separator } from '@/components/ui/separator';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import dayjs from 'dayjs';
import { getTimezoneList } from '@/utils/date';
export const Route = createFileRoute('/settings/workspace')({
beforeLoad: routeAuthBeforeLoad,
@ -71,7 +62,7 @@ const columnHelper = createColumnHelper<MemberInfo>();
function PageComponent() {
const { t } = useTranslation();
const { id: workspaceId, name, role, settings } = useCurrentWorkspace();
const { id: workspaceId, name, role } = useCurrentWorkspace();
const hasAdminPermission = useHasAdminPermission();
const { data: members = [], refetch: refetchMembers } =
trpc.workspace.members.useQuery({
@ -80,9 +71,6 @@ function PageComponent() {
const updateCurrentWorkspaceName = useUserStore(
(state) => state.updateCurrentWorkspaceName
);
const updateCurrentWorkspaceSettings = useUserStore(
(state) => state.updateCurrentWorkspaceSettings
);
const form = useForm<InviteFormValues>({
resolver: zodResolver(inviteFormSchema),
defaultValues: {
@ -101,10 +89,6 @@ function PageComponent() {
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const updateSettings = trpc.workspace.updateSettings.useMutation({
onSuccess: defaultSuccessHandler,
onError: defaultErrorHandler,
});
const [renameWorkspaceName, setRenameWorkspaceName] = useState('');
const [handleRename, isRenameLoading] = useEventWithLoading(async () => {
@ -128,19 +112,6 @@ function PageComponent() {
}
);
const handleUpdateSettings = useEvent(async (key: string, value: string) => {
const { settings } = await updateSettings.mutateAsync({
workspaceId,
settings: {
[key]: value,
},
});
updateCurrentWorkspaceSettings(settings);
});
const timezoneList = useMemo(() => getTimezoneList(), []);
const columns = useMemo(() => {
return [
columnHelper.accessor(
@ -196,36 +167,6 @@ function PageComponent() {
</CardContent>
</Card>
<Card>
<CardHeader className="text-lg font-bold">
{t('General')}
</CardHeader>
<CardContent>
<div className="flex items-center gap-4">
<div className="flex-1">{t('Timezone')}</div>
<div>
<Select
value={settings['timezone'] ?? dayjs.tz.guess()}
onValueChange={(value) =>
handleUpdateSettings('timezone', value)
}
>
<SelectTrigger className="w-[240px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
{timezoneList.map((item) => (
<SelectItem key={item.value} value={item.value}>
{item.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</CardContent>
</Card>
<Form {...form}>
<form
onSubmit={form.handleSubmit(handleInvite)}

View File

@ -10,7 +10,6 @@ export type UserLoginInfo = NonNullable<AppRouterOutput['user']['info']>;
interface UserState {
info: UserLoginInfo | null;
updateCurrentWorkspaceName: (name: string) => void;
updateCurrentWorkspaceSettings: (settings: Record<string, any>) => void;
}
export const useUserStore = createWithEqualityFn<UserState>()(
@ -28,21 +27,6 @@ export const useUserStore = createWithEqualityFn<UserState>()(
}
});
},
updateCurrentWorkspaceSettings: (settings) => {
set((state) => {
const currentUserInfo = useUserStore.getState().info;
if (!currentUserInfo) {
return;
}
for (const workspace of state.info?.workspaces ?? []) {
workspace.workspace.settings = {
...workspace.workspace.settings,
...settings,
};
}
});
},
})),
shallow
);
@ -104,7 +88,6 @@ export function useCurrentWorkspaceSafe() {
id: currentWorkspace.workspace.id,
name: currentWorkspace.workspace.name,
role: currentWorkspace.role,
settings: currentWorkspace.workspace.settings,
};
});

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,3 @@
import { setupI18nInstance } from '@i18next-toolkit/react';
export const languages = [
{
label: 'English',
@ -7,36 +5,31 @@ export const languages = [
},
{
label: 'Deutsch',
key: 'de-DE',
key: 'de',
},
{
label: 'Français',
key: 'fr-FR',
key: 'fr',
},
{
label: '日本語',
key: 'ja-JP',
key: 'jp',
},
{
label: 'Polski',
key: 'pl-PL',
key: 'pl',
},
{
label: 'Português',
key: 'pt-PT',
key: 'pt',
},
{
label: 'Русский',
key: 'ru-RU',
key: 'ru',
},
{
label: '简体中文',
key: 'zh-CN',
key: 'zh',
},
];
export function initI18N() {
setupI18nInstance({
supportedLngs: languages.map((l) => l.key),
});
}

View File

@ -1,10 +0,0 @@
import { describe, expect, test } from 'vitest';
import { getTimezoneList } from './date';
describe('getTimezoneList', () => {
test('should return timezone list with correct labels and values', () => {
const result = getTimezoneList();
expect(result).toMatchSnapshot();
});
});

View File

@ -68,25 +68,3 @@ export function formatDateWithUnit(val: dayjs.ConfigType, unit: DateUnit) {
return formatDate(val);
}
function formatOffset(offset: number) {
const sign = offset >= 0 ? '+' : '-';
const absOffset = Math.abs(offset);
const hours = String(Math.floor(absOffset / 60)).padStart(2, '0');
const minutes = String(absOffset % 60).padStart(2, '0');
return `${sign}${hours}:${minutes}`;
}
export function getTimezoneList() {
const timezones = Intl.supportedValuesOf('timeZone');
return timezones.map((timezone) => {
const offset = dayjs().tz(timezone).utcOffset();
return {
label: `${timezone} (${formatOffset(offset)})`,
value: timezone,
};
});
}

View File

@ -10,7 +10,7 @@ export function parseHealthStatusByPercent(
percent: number,
count: number
): HealthStatus {
if (percent >= 95) {
if (percent === 100) {
return 'health';
} else if (percent === 0 && count === 0) {
return 'none';

View File

@ -2,6 +2,7 @@ import express from 'express';
import 'express-async-errors';
import compression from 'compression';
import swaggerUI from 'swagger-ui-express';
import passport from 'passport';
import morgan from 'morgan';
import { websiteRouter } from './router/website.js';
import { telemetryRouter } from './router/telemetry.js';
@ -21,22 +22,17 @@ import path from 'path';
import { monitorPageManager } from './model/monitor/page/manager.js';
import { ExpressAuth } from '@auth/express';
import { authConfig } from './model/auth.js';
import { prometheusApiVersion } from './middleware/prometheus/index.js';
import { billingRouter } from './router/billing.js';
const app = express();
app.set('trust proxy', true);
app.use(prometheusApiVersion());
app.use(compression());
app.use(
express.json({
limit: '10mb',
verify: (req, res, buf) => {
(req as any).rawBody = buf;
},
})
);
app.use(passport.initialize());
app.use(morgan('tiny'));
app.use(cors());
@ -53,7 +49,6 @@ app.use(
app.use('/health', healthRouter);
app.use('/api/auth/*', ExpressAuth(authConfig));
app.use('/api/website', websiteRouter);
app.use('/api/billing', billingRouter);
app.use('/monitor', monitorRouter);
app.use('/telemetry', telemetryRouter);
app.use('/serverStatus', serverStatusRouter);

View File

@ -9,7 +9,6 @@ import { token } from '../model/notification/token/index.js';
import pMap from 'p-map';
import { sendFeedEventsNotify } from '../model/feed/event.js';
import { get } from 'lodash-es';
import { checkWorkspaceUsage } from '../model/billing/cronjob.js';
type WebsiteEventCountSqlReturn = {
workspace_id: string;
@ -30,10 +29,6 @@ export function initCronjob() {
checkFeedEventsNotify(FeedChannelNotifyFrequency.day),
]);
if (env.billing.enable) {
await checkWorkspaceUsage();
}
logger.info('Daily cronjob completed');
} catch (err) {
logger.error('Daily cronjob error:', err);
@ -247,14 +242,14 @@ async function statDailyUsage() {
}
/**
* Clear over 1 month data
* Clear over 2 week data
*/
async function clearMonitorDataDaily() {
if (env.disableAutoClear) {
return;
}
const date = dayjs().subtract(1, 'months').toDate();
const date = dayjs().subtract(2, 'weeks').toDate();
logger.info(
'[clearMonitorDataDaily] Start clear monitor data before:',
date.toISOString()
@ -274,14 +269,14 @@ async function clearMonitorDataDaily() {
}
/**
* Clear over 1 month data
* Clear over 2 week data
*/
async function clearMonitorEventDaily() {
if (env.disableAutoClear) {
return;
}
const date = dayjs().subtract(1, 'month').toDate();
const date = dayjs().subtract(2, 'weeks').toDate();
logger.info(
'[clearMonitorEventDaily] Start clear monitor data before:',
date.toISOString()
@ -391,9 +386,6 @@ async function dailyHTTPCertCheckNotify() {
);
}
/**
* Check feed events notify
*/
async function checkFeedEventsNotify(
notifyFrequency: FeedChannelNotifyFrequency
) {

View File

@ -1,14 +1,8 @@
import { version } from '@tianji/shared';
import axios from 'axios';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc.js';
import timezone from 'dayjs/plugin/timezone.js';
axios.defaults.headers.common['User-Agent'] = `tianji/${version}`;
dayjs.extend(utc);
dayjs.extend(timezone);
(BigInt.prototype as any).toJSON = function () {
const int = Number.parseInt(this.toString());
return int ?? this.toString();

View File

@ -9,7 +9,6 @@ import { initCronjob } from './cronjob/index.js';
import { logger } from './utils/logger.js';
import { app } from './app.js';
import { runMQWorker } from './mq/worker.js';
import { initCounter } from './utils/prometheus/index.js';
const port = env.port;
@ -21,8 +20,6 @@ initSocketio(httpServer);
initCronjob();
initCounter();
runMQWorker();
monitorManager.startAll();

View File

@ -1,3 +1,7 @@
import { findUser } from '../model/user.js';
import passport from 'passport';
import { Handler } from 'express';
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
import jwt from 'jsonwebtoken';
import { jwtSecret } from '../utils/common.js';
@ -10,6 +14,38 @@ export interface JWTPayload {
role: string;
}
passport.use(
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: jwtSecret,
issuer: jwtIssuer,
audience: jwtAudience,
},
function (jwt_payload, done) {
findUser(jwt_payload.id)
.then((user) => {
if (user) {
done(null, user);
} else {
done(null, false);
}
})
.catch((err) => {
done(err);
});
}
)
);
passport.serializeUser(function (user: any, cb) {
cb(null, { id: user.id, username: user.username });
});
passport.deserializeUser(function (user: any, cb) {
cb(null, user);
});
export function jwtSign(payload: JWTPayload): string {
const token = jwt.sign(
{
@ -36,3 +72,9 @@ export function jwtVerify(token: string): JWTPayload {
return payload as JWTPayload;
}
export function auth(): Handler {
return passport.authenticate('jwt', {
session: false,
});
}

View File

@ -1 +0,0 @@
This folder is fork from https://github.com/PayU/prometheus-api-metrics

View File

@ -1,132 +0,0 @@
import Prometheus from 'prom-client';
import * as utils from './utils.js';
import { SetupOptions, ApiMetricsOpts } from './types.js';
import { ExpressMiddleware } from './middleware.js';
export function prometheusApiVersion(options: ApiMetricsOpts = {}) {
const appVersion = '1.0.0';
const projectName = 'tianji';
const {
metricsPath,
defaultMetricsInterval = 10000,
durationBuckets,
requestSizeBuckets,
responseSizeBuckets,
useUniqueHistogramName,
metricsPrefix,
excludeRoutes,
includeQueryParams,
additionalLabels = [],
extractAdditionalLabelValuesFn,
} = options;
const setupOptions: SetupOptions = {};
setupOptions.metricsRoute = utils.validateInput({
input: metricsPath,
isValidInputFn: utils.isString,
defaultValue: '/_prom/metrics',
errorMessage: 'metricsPath should be an string',
});
setupOptions.excludeRoutes = utils.validateInput({
input: excludeRoutes,
isValidInputFn: utils.isArray,
defaultValue: [],
errorMessage: 'excludeRoutes should be an array',
});
setupOptions.includeQueryParams = includeQueryParams;
setupOptions.defaultMetricsInterval = defaultMetricsInterval;
setupOptions.additionalLabels = utils.validateInput({
input: additionalLabels,
isValidInputFn: utils.isArray,
defaultValue: [],
errorMessage: 'additionalLabels should be an array',
});
setupOptions.extractAdditionalLabelValuesFn = utils.validateInput({
input: extractAdditionalLabelValuesFn,
isValidInputFn: utils.isFunction,
defaultValue: () => ({}),
errorMessage: 'extractAdditionalLabelValuesFn should be a function',
});
const metricNames = utils.getMetricNames(
{
http_request_duration_seconds: 'http_request_duration_seconds',
app_version: 'app_version',
http_request_size_bytes: 'http_request_size_bytes',
http_response_size_bytes: 'http_response_size_bytes',
defaultMetricsPrefix: '',
},
useUniqueHistogramName ?? false,
metricsPrefix ?? '',
projectName
);
Prometheus.collectDefaultMetrics({
eventLoopMonitoringPrecision: defaultMetricsInterval,
prefix: `${metricNames.defaultMetricsPrefix}`,
});
PrometheusRegisterAppVersion(appVersion, metricNames.app_version);
const metricLabels = ['method', 'route', 'code', ...additionalLabels].filter(
Boolean
);
// Buckets for response time from 1ms to 500ms
const defaultDurationSecondsBuckets = [
0.001, 0.005, 0.015, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5,
];
// Buckets for request size from 5 bytes to 10000 bytes
const defaultSizeBytesBuckets = [
5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000,
];
setupOptions.responseTimeHistogram =
Prometheus.register.getSingleMetric(
metricNames.http_request_duration_seconds
) ||
new Prometheus.Histogram({
name: metricNames.http_request_duration_seconds,
help: 'Duration of HTTP requests in seconds',
labelNames: metricLabels,
buckets: durationBuckets || defaultDurationSecondsBuckets,
});
setupOptions.requestSizeHistogram =
Prometheus.register.getSingleMetric(metricNames.http_request_size_bytes) ||
new Prometheus.Histogram({
name: metricNames.http_request_size_bytes,
help: 'Size of HTTP requests in bytes',
labelNames: metricLabels,
buckets: requestSizeBuckets || defaultSizeBytesBuckets,
});
setupOptions.responseSizeHistogram =
Prometheus.register.getSingleMetric(metricNames.http_response_size_bytes) ||
new Prometheus.Histogram({
name: metricNames.http_response_size_bytes,
help: 'Size of HTTP response in bytes',
labelNames: metricLabels,
buckets: responseSizeBuckets || defaultSizeBytesBuckets,
});
const middleware = new ExpressMiddleware(setupOptions);
return middleware.middleware.bind(middleware);
}
function PrometheusRegisterAppVersion(appVersion: string, metricName: string) {
const version = new Prometheus.Gauge({
name: metricName,
help: 'The service version by package.json',
labelNames: ['version', 'major', 'minor', 'patch'],
});
const [major, minor, patch] = appVersion.split('.');
version.labels(appVersion, major, minor, patch).set(1);
}

View File

@ -1,135 +0,0 @@
import Prometheus from 'prom-client';
import { SetupOptions } from './types.js';
import { Request, Response, NextFunction } from 'express';
import * as utils from './utils.js';
export class ExpressMiddleware {
constructor(public setupOptions: SetupOptions) {}
_collectDefaultServerMetrics(timeout: number) {
const NUMBER_OF_CONNECTIONS_METRICS_NAME =
'expressjs_number_of_open_connections';
this.setupOptions.numberOfConnectionsGauge =
Prometheus.register.getSingleMetric(NUMBER_OF_CONNECTIONS_METRICS_NAME) ||
new Prometheus.Gauge({
name: NUMBER_OF_CONNECTIONS_METRICS_NAME,
help: 'Number of open connections to the Express.js server',
});
if (this.setupOptions.server) {
setInterval(this._getConnections.bind(this), timeout).unref();
}
}
_getConnections() {
if (this.setupOptions && this.setupOptions.server) {
this.setupOptions.server.getConnections((error: any, count: any) => {
if (error) {
// debug('Error while collection number of open connections', error);
} else {
this.setupOptions.numberOfConnectionsGauge.set(count);
}
});
}
}
_handleResponse(req: Request, res: Response) {
const responseLength = parseInt(res.get('Content-Length')!) || 0;
const route = this._getRoute(req);
if (
route &&
utils.shouldLogMetrics(this.setupOptions.excludeRoutes!, route)
) {
const labels = {
method: req.method,
route,
code: res.statusCode,
...this.setupOptions.extractAdditionalLabelValuesFn!(req, res),
};
this.setupOptions.requestSizeHistogram.observe(
labels,
(req as any).metrics.contentLength
);
(req as any).metrics.timer(labels);
this.setupOptions.responseSizeHistogram.observe(labels, responseLength);
}
}
_getRoute(req: Request) {
let route = req.baseUrl;
if (req.route) {
if (req.route.path !== '/') {
route = route ? route + req.route.path : req.route.path;
}
if (!route || route === '' || typeof route !== 'string') {
route = req.originalUrl.split('?')[0];
} else {
const splittedRoute = route.split('/');
const splittedUrl = req.originalUrl.split('?')[0].split('/');
const routeIndex = splittedUrl.length - splittedRoute.length + 1;
const baseUrl = splittedUrl.slice(0, routeIndex).join('/');
route = baseUrl + route;
}
if (
this.setupOptions.includeQueryParams === true &&
Object.keys(req.query).length > 0
) {
route = `${route}?${Object.keys(req.query)
.sort()
.map((queryParam) => `${queryParam}=<?>`)
.join('&')}`;
}
}
// nest.js - build request url pattern if exists
if (typeof req.params === 'object') {
Object.keys(req.params).forEach((paramName) => {
route = route.replace(req.params[paramName], ':' + paramName);
});
}
// this condition will evaluate to true only in
// express framework and no route was found for the request. if we log this metrics
// we'll risk in a memory leak since the route is not a pattern but a hardcoded string.
if (!route || route === '') {
// if (!req.route && res && res.statusCode === 404) {
route = 'N/A';
}
return route;
}
async middleware(req: Request, res: Response, next: NextFunction) {
if (!this.setupOptions.server && req.socket) {
this.setupOptions.server = (req.socket as any).server;
this._collectDefaultServerMetrics(
this.setupOptions.defaultMetricsInterval as any
);
}
const routeUrl = req.originalUrl || req.url;
if (routeUrl === this.setupOptions.metricsRoute) {
res.set('Content-Type', Prometheus.register.contentType);
return res.end(await Prometheus.register.metrics());
}
if (routeUrl === `${this.setupOptions.metricsRoute}.json`) {
return res.json(await Prometheus.register.getMetricsAsJSON());
}
(req as any).metrics = {
timer: (this.setupOptions as any).responseTimeHistogram.startTimer(),
contentLength: parseInt(req.get('content-length')!) || 0,
} as any;
res.once('finish', () => {
this._handleResponse(req, res);
});
return next();
}
}

View File

@ -1,40 +0,0 @@
import { Request, Response } from 'express';
import Prometheus from 'prom-client';
export interface ApiMetricsOpts {
metricsPath?: string;
defaultMetricsInterval?: number;
durationBuckets?: number[];
requestSizeBuckets?: number[];
responseSizeBuckets?: number[];
useUniqueHistogramName?: boolean;
metricsPrefix?: string;
excludeRoutes?: string[];
includeQueryParams?: boolean;
additionalLabels?: string[];
extractAdditionalLabelValuesFn?: (
req: Request,
res: Response
) => Record<string, unknown>;
}
export interface CollectorOpts {
durationBuckets?: number[];
countClientErrors?: boolean;
useUniqueHistogramName?: boolean;
prefix?: string;
}
export interface SetupOptions {
metricsRoute?: string;
excludeRoutes?: string[];
includeQueryParams?: boolean;
defaultMetricsInterval?: number;
additionalLabels?: string[];
extractAdditionalLabelValuesFn?: (
req: Request,
res: Response
) => Record<string, unknown>;
responseTimeHistogram?: Prometheus.Metric<string> | undefined;
[other: string]: any;
}

View File

@ -1,63 +0,0 @@
'use strict';
type MetricNames = { [key: string]: string };
const getMetricNames = (
metricNames: MetricNames,
useUniqueHistogramName: boolean,
metricsPrefix: string,
projectName: string
): MetricNames => {
const prefix = useUniqueHistogramName === true ? projectName : metricsPrefix;
if (prefix) {
Object.keys(metricNames).forEach((key) => {
metricNames[key] = `${prefix}_${metricNames[key]}`;
});
}
return metricNames;
};
const isArray = (input: unknown): input is any[] => Array.isArray(input);
const isFunction = (input: unknown): input is Function =>
typeof input === 'function';
const isString = (input: unknown): input is string => typeof input === 'string';
const shouldLogMetrics = (excludeRoutes: string[], route: string): boolean =>
excludeRoutes.every((path) => !route.includes(path));
interface ValidateInputParams<T> {
input: T | undefined;
isValidInputFn: (input: T) => boolean;
defaultValue: T;
errorMessage: string;
}
const validateInput = <T>({
input,
isValidInputFn,
defaultValue,
errorMessage,
}: ValidateInputParams<T>): T => {
if (typeof input !== 'undefined') {
if (isValidInputFn(input)) {
return input;
} else {
throw new Error(errorMessage);
}
}
return defaultValue;
};
export {
getMetricNames,
isArray,
isFunction,
isString,
shouldLogMetrics,
validateInput,
};

View File

@ -19,7 +19,6 @@ export const workspaceDashboardLayoutSchema = z.object({
export const workspaceSchema = z.object({
id: z.string(),
name: z.string(),
settings: z.record(z.string(), z.any()),
});
export const userInfoSchema = z.object({

View File

@ -1,65 +0,0 @@
import pMap from 'p-map';
import { prisma } from '../_client.js';
import { WorkspaceSubscriptionTier } from '@prisma/client';
import { logger } from '../../utils/logger.js';
import { getTierLimit } from './limit.js';
import { getWorkspaceUsage, pauseWorkspace } from './workspace.js';
import dayjs from 'dayjs';
import { getWorkspaceServiceCount } from '../workspace.js';
/**
* Check workspace usage
* if over limit, pause workspace
*/
export async function checkWorkspaceUsage() {
logger.info('[checkWorkspaceUsage] Start run checkWorkspaceUsage');
const workspaces = await prisma.workspace.findMany({
where: {
paused: false,
},
include: {
subscription: true,
},
});
await pMap(
workspaces,
async (workspace) => {
const tier =
workspace.subscription?.tier ?? WorkspaceSubscriptionTier.FREE;
if (tier === WorkspaceSubscriptionTier.UNLIMITED) {
return;
}
const [usage, serviceCount] = await Promise.all([
getWorkspaceUsage(
workspace.id,
dayjs().startOf('month').valueOf(),
dayjs().valueOf()
),
getWorkspaceServiceCount(workspace.id),
]);
const limit = getTierLimit(tier);
const overUsage =
serviceCount.website > limit.maxWebsiteCount ||
usage.websiteEventCount > limit.maxWebsiteEventCount ||
usage.monitorExecutionCount > limit.maxMonitorExecutionCount ||
usage.websiteEventCount > limit.maxWebsiteEventCount ||
usage.surveyCount > limit.maxSurveyCount ||
serviceCount.feed > limit.maxFeedChannelCount ||
usage.feedEventCount > limit.maxFeedEventCount;
if (overUsage) {
// pause workspace
await pauseWorkspace(workspace.id);
}
},
{
concurrency: 5,
}
);
}

View File

@ -1,194 +0,0 @@
import {
lemonSqueezySetup,
createCheckout,
updateSubscription,
cancelSubscription as lsCancelSubscription,
} from '@lemonsqueezy/lemonsqueezy.js';
import { env } from '../../utils/env.js';
import { prisma } from '../_client.js';
import { WorkspaceSubscriptionTier } from '@prisma/client';
export const billingAvailable = Boolean(env.billing.lemonSqueezy.apiKey);
if (billingAvailable) {
lemonSqueezySetup({
apiKey: env.billing.lemonSqueezy.apiKey,
onError: (error) => console.error('Error!', error),
});
}
export type SubscriptionTierType =
keyof typeof env.billing.lemonSqueezy.tierVariantId;
export function getTierNameByvariantId(variantId: string) {
const tierName = Object.keys(env.billing.lemonSqueezy.tierVariantId).find(
(key) =>
env.billing.lemonSqueezy.tierVariantId[key as SubscriptionTierType] ===
variantId
);
if (!tierName) {
throw new Error('Unknown Tier Name');
}
return tierName;
}
export function getTierEnumByVariantId(
variantId: string
): WorkspaceSubscriptionTier {
const name = getTierNameByvariantId(variantId);
if (name === 'free') {
return WorkspaceSubscriptionTier.FREE;
} else if (name === 'pro') {
return WorkspaceSubscriptionTier.PRO;
} else if (name === 'team') {
return WorkspaceSubscriptionTier.TEAM;
}
return WorkspaceSubscriptionTier.FREE; // not cool, fallback to free
}
export function checkIsValidProduct(storeId: string, variantId: string) {
if (String(storeId) !== env.billing.lemonSqueezy.storeId) {
return false;
}
if (
!Object.values(env.billing.lemonSqueezy.tierVariantId).includes(variantId)
) {
return false;
}
return true;
}
export async function createCheckoutBilling(
workspaceId: string,
userId: string,
subscriptionTier: SubscriptionTierType,
redirectUrl?: string
) {
const variantId = env.billing.lemonSqueezy.tierVariantId[subscriptionTier];
if (!variantId) {
throw new Error('Unknown subscription tier');
}
const userInfo = await prisma.user.findUnique({
where: {
id: userId,
},
});
if (!userInfo) {
throw new Error('User not found');
}
const subscription = await prisma.lemonSqueezySubscription.findUnique({
where: {
workspaceId,
},
});
if (subscription) {
throw new Error('This workspace already has a subscription');
}
// not existed subscription
const checkout = await createCheckout(
env.billing.lemonSqueezy.storeId,
variantId,
{
checkoutData: {
name: userInfo.nickname ?? undefined,
email: userInfo.email ?? undefined,
custom: {
userId,
workspaceId,
},
},
productOptions: {
redirectUrl,
},
}
);
if (checkout.error) {
throw checkout.error;
}
const checkoutData = checkout.data.data;
return checkoutData;
}
export async function updateWorkspaceSubscription(
workspaceId: string,
subscriptionTier: WorkspaceSubscriptionTier
) {
const res = await prisma.workspaceSubscription.upsert({
where: {
workspaceId,
},
create: {
workspaceId,
tier: subscriptionTier,
},
update: {
tier: subscriptionTier,
},
});
return res;
}
export async function changeSubscription(
workspaceId: string,
subscriptionTier: SubscriptionTierType
) {
const variantId = env.billing.lemonSqueezy.tierVariantId[subscriptionTier];
if (!variantId) {
throw new Error('Unknown subscription tier');
}
const subscription = await prisma.lemonSqueezySubscription.findUnique({
where: {
workspaceId,
},
});
if (!subscription) {
throw new Error('Can not found existed subscription');
}
const res = await updateSubscription(subscription.subscriptionId, {
variantId: Number(variantId),
});
if (res.error) {
throw res.error;
}
return res.data.data;
}
export async function cancelSubscription(workspaceId: string) {
const subscription = await prisma.lemonSqueezySubscription.findUnique({
where: {
workspaceId,
},
});
if (!subscription) {
throw new Error('Can not found existed subscription');
}
const res = await lsCancelSubscription(subscription.subscriptionId);
if (res.error) {
throw res.error;
}
return res.data.data;
}

View File

@ -1,61 +0,0 @@
import { WorkspaceSubscriptionTier } from '@prisma/client';
import { z } from 'zod';
export const TierLimitSchema = z.object({
maxWebsiteCount: z.number(),
maxWebsiteEventCount: z.number(),
maxMonitorExecutionCount: z.number(),
maxSurveyCount: z.number(),
maxFeedChannelCount: z.number(),
maxFeedEventCount: z.number(),
});
type TierLimit = z.infer<typeof TierLimitSchema>;
/**
* Limit, Every month
*/
export function getTierLimit(tier: WorkspaceSubscriptionTier): TierLimit {
if (tier === WorkspaceSubscriptionTier.FREE) {
return {
maxWebsiteCount: 3,
maxWebsiteEventCount: 100_000,
maxMonitorExecutionCount: 100_000,
maxSurveyCount: 3,
maxFeedChannelCount: 3,
maxFeedEventCount: 10_000,
};
}
if (tier === WorkspaceSubscriptionTier.PRO) {
return {
maxWebsiteCount: 10,
maxWebsiteEventCount: 1_000_000,
maxMonitorExecutionCount: 1_000_000,
maxSurveyCount: 20,
maxFeedChannelCount: 20,
maxFeedEventCount: 100_000,
};
}
if (tier === WorkspaceSubscriptionTier.TEAM) {
return {
maxWebsiteCount: -1,
maxWebsiteEventCount: 20_000_000,
maxMonitorExecutionCount: 20_000_000,
maxSurveyCount: -1,
maxFeedChannelCount: -1,
maxFeedEventCount: 1_000_000,
};
}
// Unlimited
return {
maxWebsiteCount: -1,
maxWebsiteEventCount: -1,
maxMonitorExecutionCount: -1,
maxSurveyCount: -1,
maxFeedChannelCount: -1,
maxFeedEventCount: -1,
};
}

View File

@ -1,56 +0,0 @@
import { WorkspaceSubscriptionTier } from '@prisma/client';
import { prisma } from '../_client.js';
export async function getWorkspaceUsage(
workspaceId: string,
startAt: number,
endAt: number
) {
const res = await prisma.workspaceDailyUsage.aggregate({
where: {
workspaceId,
date: {
gte: new Date(startAt),
lte: new Date(endAt),
},
},
_sum: {
websiteAcceptedCount: true,
websiteEventCount: true,
monitorExecutionCount: true,
surveyCount: true,
feedEventCount: true,
},
});
return {
websiteAcceptedCount: res._sum.websiteAcceptedCount ?? 0,
websiteEventCount: res._sum.websiteEventCount ?? 0,
monitorExecutionCount: res._sum.monitorExecutionCount ?? 0,
surveyCount: res._sum.surveyCount ?? 0,
feedEventCount: res._sum.feedEventCount ?? 0,
};
}
export async function getWorkspaceSubscription(
workspaceId: string
): Promise<WorkspaceSubscriptionTier> {
const subscription = await prisma.workspaceSubscription.findFirst({
where: {
workspaceId,
},
});
return subscription?.tier ?? WorkspaceSubscriptionTier.FREE;
}
export async function pauseWorkspace(workspaceId: string) {
await prisma.workspace.update({
where: {
id: workspaceId,
},
data: {
paused: true,
},
});
}

View File

@ -82,7 +82,7 @@ export async function getMonitorSummaryWithDay(
const list = await prisma.$queryRaw<MonitorSummaryItem[]>`
SELECT
TO_CHAR(DATE("createdAt"), 'YYYY-MM-DD') AS day,
DATE("createdAt") AS day,
COUNT(1) AS total_count,
SUM(CASE WHEN "value" >= 0 THEN 1 ELSE 0 END) AS up_count,
(SUM(CASE WHEN "value" >= 0 THEN 1 ELSE 0 END) * 100.0 / COUNT(1)) AS up_rate
@ -90,7 +90,7 @@ export async function getMonitorSummaryWithDay(
"MonitorData"
WHERE
"monitorId" = ${monitorId} AND
"createdAt" >= CURRENT_DATE - INTERVAL '1 day' * ${beforeDay}
"createdAt" >= CURRENT_DATE - INTERVAL '${beforeDay} days'
GROUP BY
DATE("createdAt")
ORDER BY

View File

@ -1,9 +1,7 @@
import { Monitor } from '@prisma/client';
import { Monitor, Notification } from '@prisma/client';
import { prisma } from '../_client.js';
import { MonitorRunner } from './runner.js';
import { logger } from '../../utils/logger.js';
import { MonitorWithNotification } from './types.js';
import { promMonitorRunnerCounter } from '../../utils/prometheus/client.js';
export type MonitorUpsertData = Pick<
Monitor,
@ -15,6 +13,8 @@ export type MonitorUpsertData = Pick<
payload: Record<string, any>;
};
type MonitorWithNotification = Monitor & { notifications: Notification[] };
export class MonitorManager {
private monitorRunner: Record<string, MonitorRunner> = {};
private isStarted = false;
@ -64,7 +64,9 @@ export class MonitorManager {
delete this.monitorRunner[monitor.id];
}
const runner = await this.createRunner(monitor);
const runner = (this.monitorRunner[monitor.id] = new MonitorRunner(
monitor
));
runner.startMonitor();
return monitor;
@ -110,7 +112,7 @@ export class MonitorManager {
Promise.all(
monitors.map(async (m) => {
try {
const runner = await this.createRunner(m);
const runner = new MonitorRunner(m);
this.monitorRunner[m.id] = runner;
await runner.startMonitor();
} catch (err) {
@ -126,37 +128,11 @@ export class MonitorManager {
return this.monitorRunner[monitorId];
}
/**
* Restart all runner basic on workspace id
*/
restartWithWorkspaceId(workspaceId: string) {
Object.values(this.monitorRunner).map((runner) => {
if (runner.workspace.id === workspaceId) {
this.createRunner(runner.monitor);
}
});
}
/**
* create runner
*/
async createRunner(monitor: MonitorWithNotification) {
if (this.monitorRunner[monitor.id]) {
this.monitorRunner[monitor.id].stopMonitor();
}
const workspace = await prisma.workspace.findUniqueOrThrow({
where: {
id: monitor.workspaceId,
},
});
createRunner(monitor: MonitorWithNotification) {
const runner = (this.monitorRunner[monitor.id] = new MonitorRunner(
workspace,
monitor
));
promMonitorRunnerCounter.set(Object.keys(this.monitorRunner).length);
return runner;
}
}

View File

@ -1,4 +1,4 @@
import { Notification, Workspace } from '@prisma/client';
import { Monitor, Notification } from '@prisma/client';
import { subscribeEventBus } from '../../ws/shared.js';
import { prisma } from '../_client.js';
import { monitorProviders } from './provider/index.js';
@ -8,8 +8,6 @@ import { logger } from '../../utils/logger.js';
import { token } from '../notification/token/index.js';
import { ContentToken } from '../notification/token/type.js';
import { createAuditLog } from '../auditLog.js';
import { MonitorWithNotification } from './types.js';
import { get } from 'lodash-es';
/**
* Class which actually run monitor data collect
@ -19,14 +17,7 @@ export class MonitorRunner {
timer: NodeJS.Timeout | null = null;
retriedNum = 0;
constructor(
public workspace: Workspace,
public monitor: MonitorWithNotification
) {}
getTimezone(): string {
return get(this.workspace, ['settings', 'timezone']) || 'utc';
}
constructor(public monitor: Monitor & { notifications: Notification[] }) {}
/**
* Start single monitor
@ -83,9 +74,9 @@ export class MonitorRunner {
);
await this.notify(`[${monitor.name}] 🔴 Down`, [
token.text(
`[${monitor.name}] 🔴 Down\nTime: ${dayjs()
.tz(this.getTimezone())
.format('YYYY-MM-DD HH:mm:ss (z)')}`
`[${monitor.name}] 🔴 Down\nTime: ${dayjs().format(
'YYYY-MM-DD HH:mm:ss (z)'
)}`
),
]);
currentStatus = 'DOWN';
@ -97,9 +88,9 @@ export class MonitorRunner {
);
await this.notify(`[${monitor.name}] ✅ Up`, [
token.text(
`[${monitor.name}] ✅ Up\nTime: ${dayjs()
.tz(this.getTimezone())
.format('YYYY-MM-DD HH:mm:ss (z)')}`
`[${monitor.name}] ✅ Up\nTime: ${dayjs().format(
'YYYY-MM-DD HH:mm:ss (z)'
)}`
),
]);
currentStatus = 'UP';

View File

@ -1,5 +0,0 @@
import { Monitor, Notification } from '@prisma/client';
export type MonitorWithNotification = Monitor & {
notifications: Notification[];
};

View File

@ -1,5 +1,4 @@
import { ServerStatusInfo } from '../../types/index.js';
import { promServerCounter } from '../utils/prometheus/client.js';
import { createSubscribeInitializer, subscribeEventBus } from '../ws/shared.js';
import { isServerOnline } from '@tianji/shared';
@ -43,13 +42,6 @@ export function recordServerStatus(info: ServerStatusInfo) {
payload,
};
promServerCounter.set(
{
workspaceId,
},
Object.keys(serverMap[workspaceId]).length
);
subscribeEventBus.emit(
'onServerStatusUpdate',
workspaceId,

View File

@ -5,9 +5,8 @@ import { jwtVerify } from '../middleware/auth.js';
import { TRPCError } from '@trpc/server';
import { Prisma } from '@prisma/client';
import { AdapterUser } from '@auth/core/adapters';
import { md5, sha256 } from '../utils/common.js';
import { md5 } from '../utils/common.js';
import { logger } from '../utils/logger.js';
import { promUserCounter } from '../utils/prometheus/client.js';
async function hashPassword(password: string) {
return await bcryptjs.hash(password, 10);
@ -42,7 +41,6 @@ export const createUserSelect = {
select: {
id: true,
name: true,
settings: true,
},
},
},
@ -89,8 +87,6 @@ export async function createAdminUser(username: string, password: string) {
return user;
});
promUserCounter.inc();
return user;
}
@ -133,8 +129,6 @@ export async function createUser(username: string, password: string) {
return user;
});
promUserCounter.inc();
return user;
}
@ -341,56 +335,3 @@ export async function leaveWorkspace(userId: string, workspaceId: string) {
throw new Error('Leave Workspace Failed.');
}
}
/**
* Generate User Api Key, for user to call api
*/
export async function generateUserApiKey(userId: string, expiredAt?: Date) {
const apiKey = `sk_${sha256(`${userId}.${Date.now()}`)}`;
const result = await prisma.userApiKey.create({
data: {
apiKey,
userId,
expiredAt,
},
});
return result.apiKey;
}
/**
* Verify User Api Key
*/
export async function verifyUserApiKey(apiKey: string) {
const result = await prisma.userApiKey.findUnique({
where: {
apiKey,
},
select: {
user: true,
expiredAt: true,
},
});
if (result?.expiredAt && result.expiredAt.valueOf() < Date.now()) {
throw new Error('Api Key has been expired.');
}
if (!result) {
throw new Error('Api Key not found');
}
prisma.userApiKey.update({
where: {
apiKey,
},
data: {
usage: {
increment: 1,
},
},
});
return result.user;
}

View File

@ -72,47 +72,3 @@ export async function getWorkspaceWebsiteDateRange(websiteId: string) {
min: res._min.createdAt,
};
}
export async function getWorkspaceServiceCount(workspaceId: string) {
const [website, monitor, telemetry, page, survey, feed] = await Promise.all([
prisma.website.count({
where: {
workspaceId,
},
}),
prisma.monitor.count({
where: {
workspaceId,
},
}),
prisma.telemetry.count({
where: {
workspaceId,
},
}),
prisma.monitorStatusPage.count({
where: {
workspaceId,
},
}),
prisma.survey.count({
where: {
workspaceId,
},
}),
prisma.feedChannel.count({
where: {
workspaceId,
},
}),
]);
return {
website,
monitor,
telemetry,
page,
survey,
feed,
};
}

View File

@ -8,7 +8,6 @@
},
"scripts": {
"dev": "tsx watch --env-file=.env ./main.ts",
"dev:debug": "tsx --inspect-brk --env-file=.env ./main.ts",
"build": "tsc",
"postinstall": "pnpm db:generate",
"check:type": "tsc --noEmit --skipLibCheck",
@ -26,7 +25,6 @@
"dependencies": {
"@auth/core": "^0.34.1",
"@auth/express": "^0.5.5",
"@lemonsqueezy/lemonsqueezy.js": "^3.3.1",
"@paralleldrive/cuid2": "^2.2.2",
"@prisma/client": "5.14.0",
"@tianji/shared": "workspace:^",
@ -44,7 +42,6 @@
"dayjs": "^1.11.9",
"detect-browser": "^5.3.0",
"dotenv": "^16.3.1",
"easy-currency-symbol": "^1.0.1",
"express": "^4.18.2",
"express-async-errors": "^3.1.1",
"express-validator": "^7.0.1",
@ -60,9 +57,9 @@
"morgan": "^1.10.0",
"nanoid": "^5.0.4",
"nodemailer": "^6.9.8",
"p-map": "4.0.0",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"ping": "^0.4.4",
"prom-client": "^15.1.3",
"puppeteer": "23.4.1",
"request-ip": "^3.3.0",
"socket.io": "^4.7.4",
@ -91,6 +88,8 @@
"@types/morgan": "^1.9.5",
"@types/node": "^18.17.12",
"@types/nodemailer": "^6.4.11",
"@types/passport": "^1.0.12",
"@types/passport-jwt": "^3.0.9",
"@types/ping": "^0.4.2",
"@types/request-ip": "^0.0.38",
"@types/supertest": "^6.0.2",
@ -98,6 +97,7 @@
"@types/tcp-ping": "^0.1.5",
"@types/uuid": "^9.0.7",
"execa": "^5.1.1",
"p-map": "4.0.0",
"prisma": "5.14.0",
"prisma-json-types-generator": "3.0.3",
"prisma-zod-generator": "0.8.13",

View File

@ -1,35 +0,0 @@
-- CreateTable
CREATE TABLE "LemonSqueezySubscription" (
"subscriptionId" TEXT NOT NULL,
"workspaceId" VARCHAR(30) NOT NULL,
"storeId" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"variantId" TEXT NOT NULL,
"status" TEXT NOT NULL,
"cardBrand" TEXT NOT NULL,
"cardLastFour" TEXT NOT NULL,
"renewsAt" TIMESTAMPTZ(6) NOT NULL,
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "LemonSqueezySubscription_pkey" PRIMARY KEY ("subscriptionId")
);
-- CreateTable
CREATE TABLE "LemonSqueezyWebhookEvent" (
"id" VARCHAR(30) NOT NULL,
"eventName" TEXT NOT NULL,
"payload" JSON NOT NULL,
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LemonSqueezyWebhookEvent_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "LemonSqueezySubscription_subscriptionId_key" ON "LemonSqueezySubscription"("subscriptionId");
-- CreateIndex
CREATE UNIQUE INDEX "LemonSqueezySubscription_workspaceId_key" ON "LemonSqueezySubscription"("workspaceId");
-- CreateIndex
CREATE UNIQUE INDEX "LemonSqueezyWebhookEvent_id_key" ON "LemonSqueezyWebhookEvent"("id");

View File

@ -1,23 +0,0 @@
-- CreateEnum
CREATE TYPE "WorkspaceSubscriptionTier" AS ENUM ('FREE', 'PRO', 'TEAM', 'UNLIMITED');
-- CreateTable
CREATE TABLE "WorkspaceSubscription" (
"id" VARCHAR(30) NOT NULL,
"workspaceId" VARCHAR(30) NOT NULL,
"tier" "WorkspaceSubscriptionTier" NOT NULL DEFAULT 'FREE',
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "WorkspaceSubscription_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "WorkspaceSubscription_workspaceId_key" ON "WorkspaceSubscription"("workspaceId");
-- AddForeignKey
ALTER TABLE "WorkspaceSubscription" ADD CONSTRAINT "WorkspaceSubscription_workspaceId_fkey" FOREIGN KEY ("workspaceId") REFERENCES "Workspace"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- Set admin workspace to UNLIMITED
INSERT INTO "WorkspaceSubscription" ("id", "workspaceId", "tier", "createdAt", "updatedAt") VALUES ('cm1yqv4xd002154qnfhzg9i5d', 'clnzoxcy10001vy2ohi4obbi0', 'UNLIMITED', '2024-10-07 08:22:45.169+00', '2024-10-07 08:22:45.169+00');

View File

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "FeedChannel" ADD COLUMN "webhookSignature" VARCHAR(100) NOT NULL DEFAULT '';

View File

@ -1,16 +0,0 @@
-- CreateTable
CREATE TABLE "UserApiKey" (
"apiKey" VARCHAR(128) NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"expiredAt" TIMESTAMP(3),
CONSTRAINT "UserApiKey_pkey" PRIMARY KEY ("apiKey")
);
-- CreateIndex
CREATE UNIQUE INDEX "UserApiKey_apiKey_key" ON "UserApiKey"("apiKey");
-- AddForeignKey
ALTER TABLE "UserApiKey" ADD CONSTRAINT "UserApiKey_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "UserApiKey" ADD COLUMN "usage" INTEGER NOT NULL DEFAULT 0;

View File

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE "Workspace" ADD COLUMN "paused" BOOLEAN NOT NULL DEFAULT false;

View File

@ -34,18 +34,6 @@ model User {
accounts Account[]
sessions Session[]
workspaces WorkspacesOnUsers[]
apiKeys UserApiKey[]
}
model UserApiKey {
apiKey String @id @unique @db.VarChar(128)
userId String
usage Int @default(0)
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
expiredAt DateTime?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model Account {
@ -96,12 +84,9 @@ model Workspace {
/// [CommonPayload]
/// @zod.custom(imports.CommonPayloadSchema)
settings Json @default("{}")
paused Boolean @default(false) // if workspace over billing, its will marked as pause and not receive and input.
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
subscription WorkspaceSubscription?
users WorkspacesOnUsers[]
websites Website[]
notifications Notification[]
@ -131,49 +116,6 @@ model WorkspacesOnUsers {
@@index([workspaceId])
}
enum WorkspaceSubscriptionTier {
FREE
PRO
TEAM
UNLIMITED // This type should only use for special people or admin workspace
}
model WorkspaceSubscription {
id String @id() @default(cuid()) @db.VarChar(30)
workspaceId String @unique @db.VarChar(30)
tier WorkspaceSubscriptionTier @default(FREE) // free, pro, team
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)
}
model LemonSqueezySubscription {
subscriptionId String @id @unique
workspaceId String @unique @db.VarChar(30)
storeId String
productId String
variantId String
status String
cardBrand String
cardLastFour String
renewsAt DateTime @db.Timestamptz(6)
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
}
model LemonSqueezyWebhookEvent {
id String @id @unique @default(cuid()) @db.VarChar(30)
eventName String
/// [CommonPayload]
/// @zod.custom(imports.CommonPayloadSchema)
payload Json @db.Json // Other payload info get from query params, should be a object
createdAt DateTime @default(now()) @db.Timestamptz(6)
}
model Website {
id String @id @unique @default(cuid()) @db.VarChar(30)
workspaceId String @db.VarChar(30)
@ -598,13 +540,12 @@ enum FeedChannelNotifyFrequency {
}
model FeedChannel {
id String @id @default(cuid()) @db.VarChar(30)
workspaceId String @db.VarChar(30)
name String
webhookSignature String @default("") @db.VarChar(100)
notifyFrequency FeedChannelNotifyFrequency @default(day)
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
id String @id @default(cuid()) @db.VarChar(30)
workspaceId String @db.VarChar(30)
name String
notifyFrequency FeedChannelNotifyFrequency @default(day)
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt @db.Timestamptz(6)
workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)
events FeedEvent[]

View File

@ -7,7 +7,6 @@ export const FeedChannelModelSchema = z.object({
id: z.string(),
workspaceId: z.string(),
name: z.string(),
webhookSignature: z.string(),
notifyFrequency: z.nativeEnum(FeedChannelNotifyFrequency),
createdAt: z.date(),
updatedAt: z.date(),

View File

@ -1,13 +1,9 @@
export * from "./user.js"
export * from "./userapikey.js"
export * from "./account.js"
export * from "./session.js"
export * from "./verificationtoken.js"
export * from "./workspace.js"
export * from "./workspacesonusers.js"
export * from "./workspacesubscription.js"
export * from "./lemonsqueezysubscription.js"
export * from "./lemonsqueezywebhookevent.js"
export * from "./website.js"
export * from "./websitesession.js"
export * from "./websiteevent.js"

View File

@ -1,16 +0,0 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
export const LemonSqueezySubscriptionModelSchema = z.object({
subscriptionId: z.string(),
workspaceId: z.string(),
storeId: z.string(),
productId: z.string(),
variantId: z.string(),
status: z.string(),
cardBrand: z.string(),
cardLastFour: z.string(),
renewsAt: z.date(),
createdAt: z.date(),
updatedAt: z.date(),
})

View File

@ -1,18 +0,0 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
// Helper schema for JSON fields
type Literal = boolean | number | string
type Json = Literal | { [key: string]: Json } | Json[]
const literalSchema = z.union([z.string(), z.number(), z.boolean()])
const jsonSchema: z.ZodSchema<Json> = z.lazy(() => z.union([literalSchema, z.array(jsonSchema), z.record(jsonSchema)]))
export const LemonSqueezyWebhookEventModelSchema = z.object({
id: z.string(),
eventName: z.string(),
/**
* [CommonPayload]
*/
payload: imports.CommonPayloadSchema,
createdAt: z.date(),
})

View File

@ -1,6 +1,6 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
import { CompleteAccount, RelatedAccountModelSchema, CompleteSession, RelatedSessionModelSchema, CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema, CompleteUserApiKey, RelatedUserApiKeyModelSchema } from "./index.js"
import { CompleteAccount, RelatedAccountModelSchema, CompleteSession, RelatedSessionModelSchema, CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema } from "./index.js"
export const UserModelSchema = z.object({
id: z.string(),
@ -21,7 +21,6 @@ export interface CompleteUser extends z.infer<typeof UserModelSchema> {
accounts: CompleteAccount[]
sessions: CompleteSession[]
workspaces: CompleteWorkspacesOnUsers[]
apiKeys: CompleteUserApiKey[]
}
/**
@ -33,5 +32,4 @@ export const RelatedUserModelSchema: z.ZodSchema<CompleteUser> = z.lazy(() => Us
accounts: RelatedAccountModelSchema.array(),
sessions: RelatedSessionModelSchema.array(),
workspaces: RelatedWorkspacesOnUsersModelSchema.array(),
apiKeys: RelatedUserApiKeyModelSchema.array(),
}))

View File

@ -1,25 +0,0 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
import { CompleteUser, RelatedUserModelSchema } from "./index.js"
export const UserApiKeyModelSchema = z.object({
apiKey: z.string(),
userId: z.string(),
usage: z.number().int(),
createdAt: z.date(),
updatedAt: z.date(),
expiredAt: z.date().nullish(),
})
export interface CompleteUserApiKey extends z.infer<typeof UserApiKeyModelSchema> {
user: CompleteUser
}
/**
* RelatedUserApiKeyModelSchema contains all relations on your model in addition to the scalars
*
* NOTE: Lazy required in case of potential circular dependencies within schema
*/
export const RelatedUserApiKeyModelSchema: z.ZodSchema<CompleteUserApiKey> = z.lazy(() => UserApiKeyModelSchema.extend({
user: RelatedUserModelSchema,
}))

View File

@ -1,6 +1,6 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
import { CompleteWorkspaceSubscription, RelatedWorkspaceSubscriptionModelSchema, CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema, CompleteWebsite, RelatedWebsiteModelSchema, CompleteNotification, RelatedNotificationModelSchema, CompleteMonitor, RelatedMonitorModelSchema, CompleteMonitorStatusPage, RelatedMonitorStatusPageModelSchema, CompleteTelemetry, RelatedTelemetryModelSchema, CompleteWorkspaceDailyUsage, RelatedWorkspaceDailyUsageModelSchema, CompleteWorkspaceAuditLog, RelatedWorkspaceAuditLogModelSchema, CompleteSurvey, RelatedSurveyModelSchema, CompleteFeedChannel, RelatedFeedChannelModelSchema } from "./index.js"
import { CompleteWorkspacesOnUsers, RelatedWorkspacesOnUsersModelSchema, CompleteWebsite, RelatedWebsiteModelSchema, CompleteNotification, RelatedNotificationModelSchema, CompleteMonitor, RelatedMonitorModelSchema, CompleteMonitorStatusPage, RelatedMonitorStatusPageModelSchema, CompleteTelemetry, RelatedTelemetryModelSchema, CompleteWorkspaceDailyUsage, RelatedWorkspaceDailyUsageModelSchema, CompleteWorkspaceAuditLog, RelatedWorkspaceAuditLogModelSchema, CompleteSurvey, RelatedSurveyModelSchema, CompleteFeedChannel, RelatedFeedChannelModelSchema } from "./index.js"
// Helper schema for JSON fields
type Literal = boolean | number | string
@ -20,13 +20,11 @@ export const WorkspaceModelSchema = z.object({
* [CommonPayload]
*/
settings: imports.CommonPayloadSchema,
paused: z.boolean(),
createdAt: z.date(),
updatedAt: z.date(),
})
export interface CompleteWorkspace extends z.infer<typeof WorkspaceModelSchema> {
subscription?: CompleteWorkspaceSubscription | null
users: CompleteWorkspacesOnUsers[]
websites: CompleteWebsite[]
notifications: CompleteNotification[]
@ -45,7 +43,6 @@ export interface CompleteWorkspace extends z.infer<typeof WorkspaceModelSchema>
* NOTE: Lazy required in case of potential circular dependencies within schema
*/
export const RelatedWorkspaceModelSchema: z.ZodSchema<CompleteWorkspace> = z.lazy(() => WorkspaceModelSchema.extend({
subscription: RelatedWorkspaceSubscriptionModelSchema.nullish(),
users: RelatedWorkspacesOnUsersModelSchema.array(),
websites: RelatedWebsiteModelSchema.array(),
notifications: RelatedNotificationModelSchema.array(),

View File

@ -1,25 +0,0 @@
import * as z from "zod"
import * as imports from "./schemas/index.js"
import { WorkspaceSubscriptionTier } from "@prisma/client"
import { CompleteWorkspace, RelatedWorkspaceModelSchema } from "./index.js"
export const WorkspaceSubscriptionModelSchema = z.object({
id: z.string(),
workspaceId: z.string(),
tier: z.nativeEnum(WorkspaceSubscriptionTier),
createdAt: z.date(),
updatedAt: z.date(),
})
export interface CompleteWorkspaceSubscription extends z.infer<typeof WorkspaceSubscriptionModelSchema> {
workspace: CompleteWorkspace
}
/**
* RelatedWorkspaceSubscriptionModelSchema contains all relations on your model in addition to the scalars
*
* NOTE: Lazy required in case of potential circular dependencies within schema
*/
export const RelatedWorkspaceSubscriptionModelSchema: z.ZodSchema<CompleteWorkspaceSubscription> = z.lazy(() => WorkspaceSubscriptionModelSchema.extend({
workspace: RelatedWorkspaceModelSchema,
}))

Some files were not shown because too many files have changed in this diff Show More