更新首页
This commit is contained in:
parent
547181d333
commit
4d02439ecb
5
.gitignore
vendored
5
.gitignore
vendored
@ -31,6 +31,7 @@ lerna-debug.log*
|
|||||||
*.swo
|
*.swo
|
||||||
*~
|
*~
|
||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
# local env (contains API keys)
|
# local env (contains API keys)
|
||||||
/.env
|
/.env
|
||||||
|
.reasonix/*
|
||||||
@ -52,6 +52,21 @@ export type News = Prisma.NewsModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Alert = Prisma.AlertModel
|
export type Alert = Prisma.AlertModel
|
||||||
|
/**
|
||||||
|
* Model ServerNode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServerNode = Prisma.ServerNodeModel
|
||||||
|
/**
|
||||||
|
* Model ServerNodeHistory
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServerNodeHistory = Prisma.ServerNodeHistoryModel
|
||||||
|
/**
|
||||||
|
* Model ServiceCheck
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServiceCheck = Prisma.ServiceCheckModel
|
||||||
/**
|
/**
|
||||||
* Model Econ_SowInventory
|
* Model Econ_SowInventory
|
||||||
*
|
*
|
||||||
|
|||||||
@ -76,6 +76,21 @@ export type News = Prisma.NewsModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Alert = Prisma.AlertModel
|
export type Alert = Prisma.AlertModel
|
||||||
|
/**
|
||||||
|
* Model ServerNode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServerNode = Prisma.ServerNodeModel
|
||||||
|
/**
|
||||||
|
* Model ServerNodeHistory
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServerNodeHistory = Prisma.ServerNodeHistoryModel
|
||||||
|
/**
|
||||||
|
* Model ServiceCheck
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ServiceCheck = Prisma.ServiceCheckModel
|
||||||
/**
|
/**
|
||||||
* Model Econ_SowInventory
|
* Model Econ_SowInventory
|
||||||
*
|
*
|
||||||
|
|||||||
@ -205,6 +205,56 @@ export type StringNullableFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SortOrderInput = {
|
||||||
|
sort: Prisma.SortOrder
|
||||||
|
nulls?: Prisma.NullsOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: string[] | null
|
||||||
|
notIn?: string[] | null
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
search?: string
|
||||||
|
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BigIntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type JsonNullableFilter<$PrismaModel = never> =
|
export type JsonNullableFilter<$PrismaModel = never> =
|
||||||
| Prisma.PatchUndefined<
|
| Prisma.PatchUndefined<
|
||||||
Prisma.Either<Required<JsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
Prisma.Either<Required<JsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
@ -229,29 +279,6 @@ export type JsonNullableFilterBase<$PrismaModel = never> = {
|
|||||||
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SortOrderInput = {
|
|
||||||
sort: Prisma.SortOrder
|
|
||||||
nulls?: Prisma.NullsOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: string[] | null
|
|
||||||
notIn?: string[] | null
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
search?: string
|
|
||||||
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type JsonNullableWithAggregatesFilter<$PrismaModel = never> =
|
export type JsonNullableWithAggregatesFilter<$PrismaModel = never> =
|
||||||
| Prisma.PatchUndefined<
|
| Prisma.PatchUndefined<
|
||||||
Prisma.Either<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
Prisma.Either<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
@ -472,6 +499,33 @@ export type NestedIntNullableFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedBigIntFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntFilter<$PrismaModel> | bigint | number
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedBigIntWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
in?: bigint[] | number[]
|
||||||
|
notIn?: bigint[] | number[]
|
||||||
|
lt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
lte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gt?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
gte?: bigint | number | Prisma.BigIntFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBigIntWithAggregatesFilter<$PrismaModel> | bigint | number
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
|
||||||
|
_sum?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBigIntFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedJsonNullableFilter<$PrismaModel = never> =
|
export type NestedJsonNullableFilter<$PrismaModel = never> =
|
||||||
| Prisma.PatchUndefined<
|
| Prisma.PatchUndefined<
|
||||||
Prisma.Either<Required<NestedJsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
Prisma.Either<Required<NestedJsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -391,6 +391,9 @@ export const ModelName = {
|
|||||||
IndicatorPoint: 'IndicatorPoint',
|
IndicatorPoint: 'IndicatorPoint',
|
||||||
News: 'News',
|
News: 'News',
|
||||||
Alert: 'Alert',
|
Alert: 'Alert',
|
||||||
|
ServerNode: 'ServerNode',
|
||||||
|
ServerNodeHistory: 'ServerNodeHistory',
|
||||||
|
ServiceCheck: 'ServiceCheck',
|
||||||
Econ_SowInventory: 'Econ_SowInventory',
|
Econ_SowInventory: 'Econ_SowInventory',
|
||||||
ScraperCache: 'ScraperCache'
|
ScraperCache: 'ScraperCache'
|
||||||
} as const
|
} as const
|
||||||
@ -408,7 +411,7 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
omit: GlobalOmitOptions
|
omit: GlobalOmitOptions
|
||||||
}
|
}
|
||||||
meta: {
|
meta: {
|
||||||
modelProps: "user" | "article" | "strategy" | "indicator" | "indicatorPoint" | "news" | "alert" | "econ_SowInventory" | "scraperCache"
|
modelProps: "user" | "article" | "strategy" | "indicator" | "indicatorPoint" | "news" | "alert" | "serverNode" | "serverNodeHistory" | "serviceCheck" | "econ_SowInventory" | "scraperCache"
|
||||||
txIsolationLevel: TransactionIsolationLevel
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
}
|
}
|
||||||
model: {
|
model: {
|
||||||
@ -874,6 +877,204 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ServerNode: {
|
||||||
|
payload: Prisma.$ServerNodePayload<ExtArgs>
|
||||||
|
fields: Prisma.ServerNodeFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.ServerNodeFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.ServerNodeFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.ServerNodeFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.ServerNodeFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.ServerNodeFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.ServerNodeCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.ServerNodeCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.ServerNodeDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.ServerNodeUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.ServerNodeDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.ServerNodeUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.ServerNodeUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodePayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.ServerNodeAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateServerNode>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.ServerNodeGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServerNodeGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.ServerNodeCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServerNodeCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServerNodeHistory: {
|
||||||
|
payload: Prisma.$ServerNodeHistoryPayload<ExtArgs>
|
||||||
|
fields: Prisma.ServerNodeHistoryFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.ServerNodeHistoryFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.ServerNodeHistoryFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.ServerNodeHistoryFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.ServerNodeHistoryFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.ServerNodeHistoryFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.ServerNodeHistoryCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.ServerNodeHistoryCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.ServerNodeHistoryDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.ServerNodeHistoryUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.ServerNodeHistoryDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.ServerNodeHistoryUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.ServerNodeHistoryUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServerNodeHistoryPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.ServerNodeHistoryAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateServerNodeHistory>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.ServerNodeHistoryGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServerNodeHistoryGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.ServerNodeHistoryCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServerNodeHistoryCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ServiceCheck: {
|
||||||
|
payload: Prisma.$ServiceCheckPayload<ExtArgs>
|
||||||
|
fields: Prisma.ServiceCheckFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.ServiceCheckFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.ServiceCheckFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.ServiceCheckFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.ServiceCheckFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.ServiceCheckFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.ServiceCheckCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.ServiceCheckCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.ServiceCheckDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.ServiceCheckUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.ServiceCheckDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.ServiceCheckUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.ServiceCheckUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ServiceCheckPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.ServiceCheckAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateServiceCheck>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.ServiceCheckGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServiceCheckGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.ServiceCheckCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ServiceCheckCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Econ_SowInventory: {
|
Econ_SowInventory: {
|
||||||
payload: Prisma.$Econ_SowInventoryPayload<ExtArgs>
|
payload: Prisma.$Econ_SowInventoryPayload<ExtArgs>
|
||||||
fields: Prisma.Econ_SowInventoryFieldRefs
|
fields: Prisma.Econ_SowInventoryFieldRefs
|
||||||
@ -1114,6 +1315,7 @@ export const NewsScalarFieldEnum = {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
summary: 'summary',
|
summary: 'summary',
|
||||||
|
content: 'content',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
url: 'url',
|
url: 'url',
|
||||||
publishedAt: 'publishedAt',
|
publishedAt: 'publishedAt',
|
||||||
@ -1140,6 +1342,70 @@ export const AlertScalarFieldEnum = {
|
|||||||
export type AlertScalarFieldEnum = (typeof AlertScalarFieldEnum)[keyof typeof AlertScalarFieldEnum]
|
export type AlertScalarFieldEnum = (typeof AlertScalarFieldEnum)[keyof typeof AlertScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
uuid: 'uuid',
|
||||||
|
name: 'name',
|
||||||
|
cpuName: 'cpuName',
|
||||||
|
os: 'os',
|
||||||
|
region: 'region',
|
||||||
|
memTotal: 'memTotal',
|
||||||
|
diskTotal: 'diskTotal',
|
||||||
|
cpuCores: 'cpuCores',
|
||||||
|
cpuUsage: 'cpuUsage',
|
||||||
|
ramUsed: 'ramUsed',
|
||||||
|
swapUsed: 'swapUsed',
|
||||||
|
load1: 'load1',
|
||||||
|
load5: 'load5',
|
||||||
|
load15: 'load15',
|
||||||
|
diskUsed: 'diskUsed',
|
||||||
|
netIn: 'netIn',
|
||||||
|
netOut: 'netOut',
|
||||||
|
uptime: 'uptime',
|
||||||
|
process: 'process',
|
||||||
|
connections: 'connections',
|
||||||
|
status: 'status',
|
||||||
|
recordedAt: 'recordedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeScalarFieldEnum = (typeof ServerNodeScalarFieldEnum)[keyof typeof ServerNodeScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeHistoryScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
uuid: 'uuid',
|
||||||
|
period: 'period',
|
||||||
|
cpu: 'cpu',
|
||||||
|
ram: 'ram',
|
||||||
|
load: 'load',
|
||||||
|
disk: 'disk',
|
||||||
|
netIn: 'netIn',
|
||||||
|
netOut: 'netOut',
|
||||||
|
process: 'process',
|
||||||
|
connections: 'connections',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeHistoryScalarFieldEnum = (typeof ServerNodeHistoryScalarFieldEnum)[keyof typeof ServerNodeHistoryScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServiceCheckScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
url: 'url',
|
||||||
|
status: 'status',
|
||||||
|
latencyMs: 'latencyMs',
|
||||||
|
message: 'message',
|
||||||
|
lastCheckAt: 'lastCheckAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServiceCheckScalarFieldEnum = (typeof ServiceCheckScalarFieldEnum)[keyof typeof ServiceCheckScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const Econ_SowInventoryScalarFieldEnum = {
|
export const Econ_SowInventoryScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
month: 'month',
|
month: 'month',
|
||||||
@ -1250,9 +1516,18 @@ export const IndicatorPointOrderByRelevanceFieldEnum = {
|
|||||||
export type IndicatorPointOrderByRelevanceFieldEnum = (typeof IndicatorPointOrderByRelevanceFieldEnum)[keyof typeof IndicatorPointOrderByRelevanceFieldEnum]
|
export type IndicatorPointOrderByRelevanceFieldEnum = (typeof IndicatorPointOrderByRelevanceFieldEnum)[keyof typeof IndicatorPointOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
||||||
export const NewsOrderByRelevanceFieldEnum = {
|
export const NewsOrderByRelevanceFieldEnum = {
|
||||||
title: 'title',
|
title: 'title',
|
||||||
summary: 'summary',
|
summary: 'summary',
|
||||||
|
content: 'content',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
url: 'url'
|
url: 'url'
|
||||||
} as const
|
} as const
|
||||||
@ -1270,6 +1545,35 @@ export const AlertOrderByRelevanceFieldEnum = {
|
|||||||
export type AlertOrderByRelevanceFieldEnum = (typeof AlertOrderByRelevanceFieldEnum)[keyof typeof AlertOrderByRelevanceFieldEnum]
|
export type AlertOrderByRelevanceFieldEnum = (typeof AlertOrderByRelevanceFieldEnum)[keyof typeof AlertOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeOrderByRelevanceFieldEnum = {
|
||||||
|
uuid: 'uuid',
|
||||||
|
name: 'name',
|
||||||
|
cpuName: 'cpuName',
|
||||||
|
os: 'os',
|
||||||
|
region: 'region',
|
||||||
|
status: 'status'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeOrderByRelevanceFieldEnum = (typeof ServerNodeOrderByRelevanceFieldEnum)[keyof typeof ServerNodeOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeHistoryOrderByRelevanceFieldEnum = {
|
||||||
|
uuid: 'uuid'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeHistoryOrderByRelevanceFieldEnum = (typeof ServerNodeHistoryOrderByRelevanceFieldEnum)[keyof typeof ServerNodeHistoryOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServiceCheckOrderByRelevanceFieldEnum = {
|
||||||
|
name: 'name',
|
||||||
|
url: 'url',
|
||||||
|
status: 'status',
|
||||||
|
message: 'message'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServiceCheckOrderByRelevanceFieldEnum = (typeof ServiceCheckOrderByRelevanceFieldEnum)[keyof typeof ServiceCheckOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
||||||
month: 'month'
|
month: 'month'
|
||||||
} as const
|
} as const
|
||||||
@ -1277,14 +1581,6 @@ export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
|||||||
export type Econ_SowInventoryOrderByRelevanceFieldEnum = (typeof Econ_SowInventoryOrderByRelevanceFieldEnum)[keyof typeof Econ_SowInventoryOrderByRelevanceFieldEnum]
|
export type Econ_SowInventoryOrderByRelevanceFieldEnum = (typeof Econ_SowInventoryOrderByRelevanceFieldEnum)[keyof typeof Econ_SowInventoryOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const NullsOrder = {
|
|
||||||
first: 'first',
|
|
||||||
last: 'last'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
|
||||||
|
|
||||||
|
|
||||||
export const ScraperCacheOrderByRelevanceFieldEnum = {
|
export const ScraperCacheOrderByRelevanceFieldEnum = {
|
||||||
scraperName: 'scraperName',
|
scraperName: 'scraperName',
|
||||||
lastDataHash: 'lastDataHash'
|
lastDataHash: 'lastDataHash'
|
||||||
@ -1347,6 +1643,13 @@ export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel,
|
|||||||
export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float'>
|
export type FloatFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Float'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'BigInt'
|
||||||
|
*/
|
||||||
|
export type BigIntFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'BigInt'>
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch Payload for updateMany & deleteMany & createMany
|
* Batch Payload for updateMany & deleteMany & createMany
|
||||||
*/
|
*/
|
||||||
@ -1449,6 +1752,9 @@ export type GlobalOmitConfig = {
|
|||||||
indicatorPoint?: Prisma.IndicatorPointOmit
|
indicatorPoint?: Prisma.IndicatorPointOmit
|
||||||
news?: Prisma.NewsOmit
|
news?: Prisma.NewsOmit
|
||||||
alert?: Prisma.AlertOmit
|
alert?: Prisma.AlertOmit
|
||||||
|
serverNode?: Prisma.ServerNodeOmit
|
||||||
|
serverNodeHistory?: Prisma.ServerNodeHistoryOmit
|
||||||
|
serviceCheck?: Prisma.ServiceCheckOmit
|
||||||
econ_SowInventory?: Prisma.Econ_SowInventoryOmit
|
econ_SowInventory?: Prisma.Econ_SowInventoryOmit
|
||||||
scraperCache?: Prisma.ScraperCacheOmit
|
scraperCache?: Prisma.ScraperCacheOmit
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,6 +58,9 @@ export const ModelName = {
|
|||||||
IndicatorPoint: 'IndicatorPoint',
|
IndicatorPoint: 'IndicatorPoint',
|
||||||
News: 'News',
|
News: 'News',
|
||||||
Alert: 'Alert',
|
Alert: 'Alert',
|
||||||
|
ServerNode: 'ServerNode',
|
||||||
|
ServerNodeHistory: 'ServerNodeHistory',
|
||||||
|
ServiceCheck: 'ServiceCheck',
|
||||||
Econ_SowInventory: 'Econ_SowInventory',
|
Econ_SowInventory: 'Econ_SowInventory',
|
||||||
ScraperCache: 'ScraperCache'
|
ScraperCache: 'ScraperCache'
|
||||||
} as const
|
} as const
|
||||||
@ -147,6 +150,7 @@ export const NewsScalarFieldEnum = {
|
|||||||
id: 'id',
|
id: 'id',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
summary: 'summary',
|
summary: 'summary',
|
||||||
|
content: 'content',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
url: 'url',
|
url: 'url',
|
||||||
publishedAt: 'publishedAt',
|
publishedAt: 'publishedAt',
|
||||||
@ -173,6 +177,70 @@ export const AlertScalarFieldEnum = {
|
|||||||
export type AlertScalarFieldEnum = (typeof AlertScalarFieldEnum)[keyof typeof AlertScalarFieldEnum]
|
export type AlertScalarFieldEnum = (typeof AlertScalarFieldEnum)[keyof typeof AlertScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
uuid: 'uuid',
|
||||||
|
name: 'name',
|
||||||
|
cpuName: 'cpuName',
|
||||||
|
os: 'os',
|
||||||
|
region: 'region',
|
||||||
|
memTotal: 'memTotal',
|
||||||
|
diskTotal: 'diskTotal',
|
||||||
|
cpuCores: 'cpuCores',
|
||||||
|
cpuUsage: 'cpuUsage',
|
||||||
|
ramUsed: 'ramUsed',
|
||||||
|
swapUsed: 'swapUsed',
|
||||||
|
load1: 'load1',
|
||||||
|
load5: 'load5',
|
||||||
|
load15: 'load15',
|
||||||
|
diskUsed: 'diskUsed',
|
||||||
|
netIn: 'netIn',
|
||||||
|
netOut: 'netOut',
|
||||||
|
uptime: 'uptime',
|
||||||
|
process: 'process',
|
||||||
|
connections: 'connections',
|
||||||
|
status: 'status',
|
||||||
|
recordedAt: 'recordedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeScalarFieldEnum = (typeof ServerNodeScalarFieldEnum)[keyof typeof ServerNodeScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeHistoryScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
uuid: 'uuid',
|
||||||
|
period: 'period',
|
||||||
|
cpu: 'cpu',
|
||||||
|
ram: 'ram',
|
||||||
|
load: 'load',
|
||||||
|
disk: 'disk',
|
||||||
|
netIn: 'netIn',
|
||||||
|
netOut: 'netOut',
|
||||||
|
process: 'process',
|
||||||
|
connections: 'connections',
|
||||||
|
createdAt: 'createdAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeHistoryScalarFieldEnum = (typeof ServerNodeHistoryScalarFieldEnum)[keyof typeof ServerNodeHistoryScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServiceCheckScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
url: 'url',
|
||||||
|
status: 'status',
|
||||||
|
latencyMs: 'latencyMs',
|
||||||
|
message: 'message',
|
||||||
|
lastCheckAt: 'lastCheckAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServiceCheckScalarFieldEnum = (typeof ServiceCheckScalarFieldEnum)[keyof typeof ServiceCheckScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const Econ_SowInventoryScalarFieldEnum = {
|
export const Econ_SowInventoryScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
month: 'month',
|
month: 'month',
|
||||||
@ -283,9 +351,18 @@ export const IndicatorPointOrderByRelevanceFieldEnum = {
|
|||||||
export type IndicatorPointOrderByRelevanceFieldEnum = (typeof IndicatorPointOrderByRelevanceFieldEnum)[keyof typeof IndicatorPointOrderByRelevanceFieldEnum]
|
export type IndicatorPointOrderByRelevanceFieldEnum = (typeof IndicatorPointOrderByRelevanceFieldEnum)[keyof typeof IndicatorPointOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const NullsOrder = {
|
||||||
|
first: 'first',
|
||||||
|
last: 'last'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
||||||
|
|
||||||
|
|
||||||
export const NewsOrderByRelevanceFieldEnum = {
|
export const NewsOrderByRelevanceFieldEnum = {
|
||||||
title: 'title',
|
title: 'title',
|
||||||
summary: 'summary',
|
summary: 'summary',
|
||||||
|
content: 'content',
|
||||||
source: 'source',
|
source: 'source',
|
||||||
url: 'url'
|
url: 'url'
|
||||||
} as const
|
} as const
|
||||||
@ -303,6 +380,35 @@ export const AlertOrderByRelevanceFieldEnum = {
|
|||||||
export type AlertOrderByRelevanceFieldEnum = (typeof AlertOrderByRelevanceFieldEnum)[keyof typeof AlertOrderByRelevanceFieldEnum]
|
export type AlertOrderByRelevanceFieldEnum = (typeof AlertOrderByRelevanceFieldEnum)[keyof typeof AlertOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeOrderByRelevanceFieldEnum = {
|
||||||
|
uuid: 'uuid',
|
||||||
|
name: 'name',
|
||||||
|
cpuName: 'cpuName',
|
||||||
|
os: 'os',
|
||||||
|
region: 'region',
|
||||||
|
status: 'status'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeOrderByRelevanceFieldEnum = (typeof ServerNodeOrderByRelevanceFieldEnum)[keyof typeof ServerNodeOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServerNodeHistoryOrderByRelevanceFieldEnum = {
|
||||||
|
uuid: 'uuid'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServerNodeHistoryOrderByRelevanceFieldEnum = (typeof ServerNodeHistoryOrderByRelevanceFieldEnum)[keyof typeof ServerNodeHistoryOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ServiceCheckOrderByRelevanceFieldEnum = {
|
||||||
|
name: 'name',
|
||||||
|
url: 'url',
|
||||||
|
status: 'status',
|
||||||
|
message: 'message'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ServiceCheckOrderByRelevanceFieldEnum = (typeof ServiceCheckOrderByRelevanceFieldEnum)[keyof typeof ServiceCheckOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
||||||
month: 'month'
|
month: 'month'
|
||||||
} as const
|
} as const
|
||||||
@ -310,14 +416,6 @@ export const Econ_SowInventoryOrderByRelevanceFieldEnum = {
|
|||||||
export type Econ_SowInventoryOrderByRelevanceFieldEnum = (typeof Econ_SowInventoryOrderByRelevanceFieldEnum)[keyof typeof Econ_SowInventoryOrderByRelevanceFieldEnum]
|
export type Econ_SowInventoryOrderByRelevanceFieldEnum = (typeof Econ_SowInventoryOrderByRelevanceFieldEnum)[keyof typeof Econ_SowInventoryOrderByRelevanceFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const NullsOrder = {
|
|
||||||
first: 'first',
|
|
||||||
last: 'last'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]
|
|
||||||
|
|
||||||
|
|
||||||
export const ScraperCacheOrderByRelevanceFieldEnum = {
|
export const ScraperCacheOrderByRelevanceFieldEnum = {
|
||||||
scraperName: 'scraperName',
|
scraperName: 'scraperName',
|
||||||
lastDataHash: 'lastDataHash'
|
lastDataHash: 'lastDataHash'
|
||||||
|
|||||||
@ -15,6 +15,9 @@ export type * from './models/Indicator.ts'
|
|||||||
export type * from './models/IndicatorPoint.ts'
|
export type * from './models/IndicatorPoint.ts'
|
||||||
export type * from './models/News.ts'
|
export type * from './models/News.ts'
|
||||||
export type * from './models/Alert.ts'
|
export type * from './models/Alert.ts'
|
||||||
|
export type * from './models/ServerNode.ts'
|
||||||
|
export type * from './models/ServerNodeHistory.ts'
|
||||||
|
export type * from './models/ServiceCheck.ts'
|
||||||
export type * from './models/Econ_SowInventory.ts'
|
export type * from './models/Econ_SowInventory.ts'
|
||||||
export type * from './models/ScraperCache.ts'
|
export type * from './models/ScraperCache.ts'
|
||||||
export type * from './commonInputTypes.ts'
|
export type * from './commonInputTypes.ts'
|
||||||
@ -40,6 +40,7 @@ export type NewsMinAggregateOutputType = {
|
|||||||
id: number | null
|
id: number | null
|
||||||
title: string | null
|
title: string | null
|
||||||
summary: string | null
|
summary: string | null
|
||||||
|
content: string | null
|
||||||
source: string | null
|
source: string | null
|
||||||
url: string | null
|
url: string | null
|
||||||
publishedAt: Date | null
|
publishedAt: Date | null
|
||||||
@ -51,6 +52,7 @@ export type NewsMaxAggregateOutputType = {
|
|||||||
id: number | null
|
id: number | null
|
||||||
title: string | null
|
title: string | null
|
||||||
summary: string | null
|
summary: string | null
|
||||||
|
content: string | null
|
||||||
source: string | null
|
source: string | null
|
||||||
url: string | null
|
url: string | null
|
||||||
publishedAt: Date | null
|
publishedAt: Date | null
|
||||||
@ -62,6 +64,7 @@ export type NewsCountAggregateOutputType = {
|
|||||||
id: number
|
id: number
|
||||||
title: number
|
title: number
|
||||||
summary: number
|
summary: number
|
||||||
|
content: number
|
||||||
source: number
|
source: number
|
||||||
url: number
|
url: number
|
||||||
publishedAt: number
|
publishedAt: number
|
||||||
@ -85,6 +88,7 @@ export type NewsMinAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
title?: true
|
title?: true
|
||||||
summary?: true
|
summary?: true
|
||||||
|
content?: true
|
||||||
source?: true
|
source?: true
|
||||||
url?: true
|
url?: true
|
||||||
publishedAt?: true
|
publishedAt?: true
|
||||||
@ -96,6 +100,7 @@ export type NewsMaxAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
title?: true
|
title?: true
|
||||||
summary?: true
|
summary?: true
|
||||||
|
content?: true
|
||||||
source?: true
|
source?: true
|
||||||
url?: true
|
url?: true
|
||||||
publishedAt?: true
|
publishedAt?: true
|
||||||
@ -107,6 +112,7 @@ export type NewsCountAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
title?: true
|
title?: true
|
||||||
summary?: true
|
summary?: true
|
||||||
|
content?: true
|
||||||
source?: true
|
source?: true
|
||||||
url?: true
|
url?: true
|
||||||
publishedAt?: true
|
publishedAt?: true
|
||||||
@ -205,6 +211,7 @@ export type NewsGroupByOutputType = {
|
|||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
summary: string
|
summary: string
|
||||||
|
content: string | null
|
||||||
source: string
|
source: string
|
||||||
url: string
|
url: string
|
||||||
publishedAt: Date
|
publishedAt: Date
|
||||||
@ -239,6 +246,7 @@ export type NewsWhereInput = {
|
|||||||
id?: Prisma.IntFilter<"News"> | number
|
id?: Prisma.IntFilter<"News"> | number
|
||||||
title?: Prisma.StringFilter<"News"> | string
|
title?: Prisma.StringFilter<"News"> | string
|
||||||
summary?: Prisma.StringFilter<"News"> | string
|
summary?: Prisma.StringFilter<"News"> | string
|
||||||
|
content?: Prisma.StringNullableFilter<"News"> | string | null
|
||||||
source?: Prisma.StringFilter<"News"> | string
|
source?: Prisma.StringFilter<"News"> | string
|
||||||
url?: Prisma.StringFilter<"News"> | string
|
url?: Prisma.StringFilter<"News"> | string
|
||||||
publishedAt?: Prisma.DateTimeFilter<"News"> | Date | string
|
publishedAt?: Prisma.DateTimeFilter<"News"> | Date | string
|
||||||
@ -250,6 +258,7 @@ export type NewsOrderByWithRelationInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
title?: Prisma.SortOrder
|
title?: Prisma.SortOrder
|
||||||
summary?: Prisma.SortOrder
|
summary?: Prisma.SortOrder
|
||||||
|
content?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
source?: Prisma.SortOrder
|
source?: Prisma.SortOrder
|
||||||
url?: Prisma.SortOrder
|
url?: Prisma.SortOrder
|
||||||
publishedAt?: Prisma.SortOrder
|
publishedAt?: Prisma.SortOrder
|
||||||
@ -266,6 +275,7 @@ export type NewsWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
NOT?: Prisma.NewsWhereInput | Prisma.NewsWhereInput[]
|
NOT?: Prisma.NewsWhereInput | Prisma.NewsWhereInput[]
|
||||||
title?: Prisma.StringFilter<"News"> | string
|
title?: Prisma.StringFilter<"News"> | string
|
||||||
summary?: Prisma.StringFilter<"News"> | string
|
summary?: Prisma.StringFilter<"News"> | string
|
||||||
|
content?: Prisma.StringNullableFilter<"News"> | string | null
|
||||||
source?: Prisma.StringFilter<"News"> | string
|
source?: Prisma.StringFilter<"News"> | string
|
||||||
url?: Prisma.StringFilter<"News"> | string
|
url?: Prisma.StringFilter<"News"> | string
|
||||||
publishedAt?: Prisma.DateTimeFilter<"News"> | Date | string
|
publishedAt?: Prisma.DateTimeFilter<"News"> | Date | string
|
||||||
@ -277,6 +287,7 @@ export type NewsOrderByWithAggregationInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
title?: Prisma.SortOrder
|
title?: Prisma.SortOrder
|
||||||
summary?: Prisma.SortOrder
|
summary?: Prisma.SortOrder
|
||||||
|
content?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
source?: Prisma.SortOrder
|
source?: Prisma.SortOrder
|
||||||
url?: Prisma.SortOrder
|
url?: Prisma.SortOrder
|
||||||
publishedAt?: Prisma.SortOrder
|
publishedAt?: Prisma.SortOrder
|
||||||
@ -296,6 +307,7 @@ export type NewsScalarWhereWithAggregatesInput = {
|
|||||||
id?: Prisma.IntWithAggregatesFilter<"News"> | number
|
id?: Prisma.IntWithAggregatesFilter<"News"> | number
|
||||||
title?: Prisma.StringWithAggregatesFilter<"News"> | string
|
title?: Prisma.StringWithAggregatesFilter<"News"> | string
|
||||||
summary?: Prisma.StringWithAggregatesFilter<"News"> | string
|
summary?: Prisma.StringWithAggregatesFilter<"News"> | string
|
||||||
|
content?: Prisma.StringNullableWithAggregatesFilter<"News"> | string | null
|
||||||
source?: Prisma.StringWithAggregatesFilter<"News"> | string
|
source?: Prisma.StringWithAggregatesFilter<"News"> | string
|
||||||
url?: Prisma.StringWithAggregatesFilter<"News"> | string
|
url?: Prisma.StringWithAggregatesFilter<"News"> | string
|
||||||
publishedAt?: Prisma.DateTimeWithAggregatesFilter<"News"> | Date | string
|
publishedAt?: Prisma.DateTimeWithAggregatesFilter<"News"> | Date | string
|
||||||
@ -306,6 +318,7 @@ export type NewsScalarWhereWithAggregatesInput = {
|
|||||||
export type NewsCreateInput = {
|
export type NewsCreateInput = {
|
||||||
title: string
|
title: string
|
||||||
summary?: string
|
summary?: string
|
||||||
|
content?: string | null
|
||||||
source?: string
|
source?: string
|
||||||
url?: string
|
url?: string
|
||||||
publishedAt: Date | string
|
publishedAt: Date | string
|
||||||
@ -317,6 +330,7 @@ export type NewsUncheckedCreateInput = {
|
|||||||
id?: number
|
id?: number
|
||||||
title: string
|
title: string
|
||||||
summary?: string
|
summary?: string
|
||||||
|
content?: string | null
|
||||||
source?: string
|
source?: string
|
||||||
url?: string
|
url?: string
|
||||||
publishedAt: Date | string
|
publishedAt: Date | string
|
||||||
@ -327,6 +341,7 @@ export type NewsUncheckedCreateInput = {
|
|||||||
export type NewsUpdateInput = {
|
export type NewsUpdateInput = {
|
||||||
title?: Prisma.StringFieldUpdateOperationsInput | string
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
source?: Prisma.StringFieldUpdateOperationsInput | string
|
source?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
url?: Prisma.StringFieldUpdateOperationsInput | string
|
url?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@ -338,6 +353,7 @@ export type NewsUncheckedUpdateInput = {
|
|||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
title?: Prisma.StringFieldUpdateOperationsInput | string
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
source?: Prisma.StringFieldUpdateOperationsInput | string
|
source?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
url?: Prisma.StringFieldUpdateOperationsInput | string
|
url?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@ -349,6 +365,7 @@ export type NewsCreateManyInput = {
|
|||||||
id?: number
|
id?: number
|
||||||
title: string
|
title: string
|
||||||
summary?: string
|
summary?: string
|
||||||
|
content?: string | null
|
||||||
source?: string
|
source?: string
|
||||||
url?: string
|
url?: string
|
||||||
publishedAt: Date | string
|
publishedAt: Date | string
|
||||||
@ -359,6 +376,7 @@ export type NewsCreateManyInput = {
|
|||||||
export type NewsUpdateManyMutationInput = {
|
export type NewsUpdateManyMutationInput = {
|
||||||
title?: Prisma.StringFieldUpdateOperationsInput | string
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
source?: Prisma.StringFieldUpdateOperationsInput | string
|
source?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
url?: Prisma.StringFieldUpdateOperationsInput | string
|
url?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@ -370,6 +388,7 @@ export type NewsUncheckedUpdateManyInput = {
|
|||||||
id?: Prisma.IntFieldUpdateOperationsInput | number
|
id?: Prisma.IntFieldUpdateOperationsInput | number
|
||||||
title?: Prisma.StringFieldUpdateOperationsInput | string
|
title?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
summary?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
content?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
source?: Prisma.StringFieldUpdateOperationsInput | string
|
source?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
url?: Prisma.StringFieldUpdateOperationsInput | string
|
url?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
publishedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@ -392,6 +411,7 @@ export type NewsCountOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
title?: Prisma.SortOrder
|
title?: Prisma.SortOrder
|
||||||
summary?: Prisma.SortOrder
|
summary?: Prisma.SortOrder
|
||||||
|
content?: Prisma.SortOrder
|
||||||
source?: Prisma.SortOrder
|
source?: Prisma.SortOrder
|
||||||
url?: Prisma.SortOrder
|
url?: Prisma.SortOrder
|
||||||
publishedAt?: Prisma.SortOrder
|
publishedAt?: Prisma.SortOrder
|
||||||
@ -408,6 +428,7 @@ export type NewsMaxOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
title?: Prisma.SortOrder
|
title?: Prisma.SortOrder
|
||||||
summary?: Prisma.SortOrder
|
summary?: Prisma.SortOrder
|
||||||
|
content?: Prisma.SortOrder
|
||||||
source?: Prisma.SortOrder
|
source?: Prisma.SortOrder
|
||||||
url?: Prisma.SortOrder
|
url?: Prisma.SortOrder
|
||||||
publishedAt?: Prisma.SortOrder
|
publishedAt?: Prisma.SortOrder
|
||||||
@ -419,6 +440,7 @@ export type NewsMinOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
title?: Prisma.SortOrder
|
title?: Prisma.SortOrder
|
||||||
summary?: Prisma.SortOrder
|
summary?: Prisma.SortOrder
|
||||||
|
content?: Prisma.SortOrder
|
||||||
source?: Prisma.SortOrder
|
source?: Prisma.SortOrder
|
||||||
url?: Prisma.SortOrder
|
url?: Prisma.SortOrder
|
||||||
publishedAt?: Prisma.SortOrder
|
publishedAt?: Prisma.SortOrder
|
||||||
@ -431,12 +453,17 @@ export type NewsSumOrderByAggregateInput = {
|
|||||||
impactRank?: Prisma.SortOrder
|
impactRank?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NullableStringFieldUpdateOperationsInput = {
|
||||||
|
set?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type NewsSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type NewsSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
title?: boolean
|
title?: boolean
|
||||||
summary?: boolean
|
summary?: boolean
|
||||||
|
content?: boolean
|
||||||
source?: boolean
|
source?: boolean
|
||||||
url?: boolean
|
url?: boolean
|
||||||
publishedAt?: boolean
|
publishedAt?: boolean
|
||||||
@ -450,6 +477,7 @@ export type NewsSelectScalar = {
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
title?: boolean
|
title?: boolean
|
||||||
summary?: boolean
|
summary?: boolean
|
||||||
|
content?: boolean
|
||||||
source?: boolean
|
source?: boolean
|
||||||
url?: boolean
|
url?: boolean
|
||||||
publishedAt?: boolean
|
publishedAt?: boolean
|
||||||
@ -457,7 +485,7 @@ export type NewsSelectScalar = {
|
|||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NewsOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "title" | "summary" | "source" | "url" | "publishedAt" | "impactRank" | "createdAt", ExtArgs["result"]["news"]>
|
export type NewsOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "title" | "summary" | "content" | "source" | "url" | "publishedAt" | "impactRank" | "createdAt", ExtArgs["result"]["news"]>
|
||||||
|
|
||||||
export type $NewsPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $NewsPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "News"
|
name: "News"
|
||||||
@ -466,6 +494,7 @@ export type $NewsPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs =
|
|||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
summary: string
|
summary: string
|
||||||
|
content: string | null
|
||||||
source: string
|
source: string
|
||||||
url: string
|
url: string
|
||||||
publishedAt: Date
|
publishedAt: Date
|
||||||
@ -843,6 +872,7 @@ export interface NewsFieldRefs {
|
|||||||
readonly id: Prisma.FieldRef<"News", 'Int'>
|
readonly id: Prisma.FieldRef<"News", 'Int'>
|
||||||
readonly title: Prisma.FieldRef<"News", 'String'>
|
readonly title: Prisma.FieldRef<"News", 'String'>
|
||||||
readonly summary: Prisma.FieldRef<"News", 'String'>
|
readonly summary: Prisma.FieldRef<"News", 'String'>
|
||||||
|
readonly content: Prisma.FieldRef<"News", 'String'>
|
||||||
readonly source: Prisma.FieldRef<"News", 'String'>
|
readonly source: Prisma.FieldRef<"News", 'String'>
|
||||||
readonly url: Prisma.FieldRef<"News", 'String'>
|
readonly url: Prisma.FieldRef<"News", 'String'>
|
||||||
readonly publishedAt: Prisma.FieldRef<"News", 'DateTime'>
|
readonly publishedAt: Prisma.FieldRef<"News", 'DateTime'>
|
||||||
|
|||||||
@ -391,10 +391,6 @@ export type ScraperCacheSumOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NullableStringFieldUpdateOperationsInput = {
|
|
||||||
set?: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type ScraperCacheSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ScraperCacheSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
|
|||||||
1702
generated/prisma/models/ServerNode.ts
Normal file
1702
generated/prisma/models/ServerNode.ts
Normal file
File diff suppressed because it is too large
Load Diff
1320
generated/prisma/models/ServerNodeHistory.ts
Normal file
1320
generated/prisma/models/ServerNodeHistory.ts
Normal file
File diff suppressed because it is too large
Load Diff
1194
generated/prisma/models/ServiceCheck.ts
Normal file
1194
generated/prisma/models/ServiceCheck.ts
Normal file
File diff suppressed because it is too large
Load Diff
24
package-lock.json
generated
24
package-lock.json
generated
@ -24,7 +24,8 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.26.0",
|
"react-router-dom": "^6.26.0",
|
||||||
"recharts": "^3.8.1"
|
"recharts": "^3.8.1",
|
||||||
|
"ws": "^8.21.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"@tauri-apps/api": "^2.0.0",
|
||||||
@ -6360,6 +6361,27 @@
|
|||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.21.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/ws/-/ws-8.21.2.tgz",
|
||||||
|
"integrity": "sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
|||||||
@ -41,7 +41,8 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.26.0",
|
"react-router-dom": "^6.26.0",
|
||||||
"recharts": "^3.8.1"
|
"recharts": "^3.8.1",
|
||||||
|
"ws": "^8.21.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/api": "^2.0.0",
|
"@tauri-apps/api": "^2.0.0",
|
||||||
|
|||||||
68
prisma/migrations/20260806140000_monitors/migration.sql
Normal file
68
prisma/migrations/20260806140000_monitors/migration.sql
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
-- 服务器监控 + 服务健康检查
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `ServerNode` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`uuid` VARCHAR(191) NOT NULL,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`cpuName` VARCHAR(191) NOT NULL DEFAULT '',
|
||||||
|
`os` VARCHAR(191) NOT NULL DEFAULT '',
|
||||||
|
`region` VARCHAR(191) NOT NULL DEFAULT '',
|
||||||
|
`memTotal` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`diskTotal` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`cpuCores` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`cpuUsage` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`ramUsed` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`swapUsed` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`load1` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`load5` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`load15` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`diskUsed` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`netIn` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`netOut` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`uptime` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`process` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`connections` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`status` VARCHAR(191) NOT NULL DEFAULT 'unknown',
|
||||||
|
`recordedAt` DATETIME(3) NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`updatedAt` DATETIME(3) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `ServerNode_uuid_key`(`uuid`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `ServerNodeHistory` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`uuid` VARCHAR(191) NOT NULL,
|
||||||
|
`period` DATETIME(3) NOT NULL,
|
||||||
|
`cpu` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`ram` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`load` DOUBLE NOT NULL DEFAULT 0,
|
||||||
|
`disk` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`netIn` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`netOut` BIGINT NOT NULL DEFAULT 0,
|
||||||
|
`process` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`connections` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
|
||||||
|
UNIQUE INDEX `ServerNodeHistory_uuid_period_key`(`uuid`, `period`),
|
||||||
|
INDEX `ServerNodeHistory_uuid_period_idx`(`uuid`, `period`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `ServiceCheck` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`url` VARCHAR(191) NOT NULL,
|
||||||
|
`status` VARCHAR(191) NOT NULL DEFAULT 'unknown',
|
||||||
|
`latencyMs` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`message` VARCHAR(191) NOT NULL DEFAULT '',
|
||||||
|
`lastCheckAt` DATETIME(3) NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`updatedAt` DATETIME(3) NOT NULL,
|
||||||
|
|
||||||
|
UNIQUE INDEX `ServiceCheck_name_key`(`name`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable (news.content 全文, 须在 news_and_alerts 建表之后执行)
|
||||||
|
ALTER TABLE `News` ADD COLUMN `content` TEXT NULL;
|
||||||
@ -1,3 +1,3 @@
|
|||||||
# Please do not edit this file manually
|
# Please do not edit this file manually
|
||||||
# It should be added in your version-control system (e.g., Git)
|
# It should be added in your version-control system (e.g., Git)
|
||||||
provider = "mysql"
|
provider = "mysql"
|
||||||
|
|||||||
@ -72,6 +72,7 @@ model News {
|
|||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
title String
|
title String
|
||||||
summary String @default("")
|
summary String @default("")
|
||||||
|
content String? @db.Text
|
||||||
source String @default("")
|
source String @default("")
|
||||||
url String @default("")
|
url String @default("")
|
||||||
publishedAt DateTime
|
publishedAt DateTime
|
||||||
@ -98,6 +99,69 @@ model Alert {
|
|||||||
@@index([createdAt])
|
@@index([createdAt])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 服务器监控: 对接 komari 公开 API
|
||||||
|
model ServerNode {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
uuid String @unique
|
||||||
|
name String
|
||||||
|
cpuName String @default("")
|
||||||
|
os String @default("")
|
||||||
|
region String @default("")
|
||||||
|
memTotal BigInt @default(0)
|
||||||
|
diskTotal BigInt @default(0)
|
||||||
|
cpuCores Int @default(0)
|
||||||
|
// 最新实时快照
|
||||||
|
cpuUsage Float @default(0)
|
||||||
|
ramUsed BigInt @default(0)
|
||||||
|
swapUsed BigInt @default(0)
|
||||||
|
load1 Float @default(0)
|
||||||
|
load5 Float @default(0)
|
||||||
|
load15 Float @default(0)
|
||||||
|
diskUsed BigInt @default(0)
|
||||||
|
netIn BigInt @default(0)
|
||||||
|
netOut BigInt @default(0)
|
||||||
|
uptime BigInt @default(0)
|
||||||
|
process Int @default(0)
|
||||||
|
connections Int @default(0)
|
||||||
|
status String @default("unknown") // online | offline | unknown
|
||||||
|
recordedAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
|
// 时序历史: 每节点每分钟一条 (komari records 1 分钟粒度)
|
||||||
|
model ServerNodeHistory {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
uuid String
|
||||||
|
period DateTime
|
||||||
|
cpu Float @default(0)
|
||||||
|
ram BigInt @default(0)
|
||||||
|
load Float @default(0)
|
||||||
|
disk BigInt @default(0)
|
||||||
|
netIn BigInt @default(0)
|
||||||
|
netOut BigInt @default(0)
|
||||||
|
process Int @default(0)
|
||||||
|
connections Int @default(0)
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
|
||||||
|
@@unique([uuid, period])
|
||||||
|
@@index([uuid, period])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服务健康检查: 本应用自检 (健康端点 / DB / 第三方)
|
||||||
|
model ServiceCheck {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @unique
|
||||||
|
url String
|
||||||
|
status String @default("unknown") // up | down | unknown
|
||||||
|
latencyMs Int @default(0)
|
||||||
|
message String @default("")
|
||||||
|
lastCheckAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
|
||||||
model Econ_SowInventory {
|
model Econ_SowInventory {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
month String @unique
|
month String @unique
|
||||||
|
|||||||
72
scripts/.fix-cron.cjs
Normal file
72
scripts/.fix-cron.cjs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
const fs = require('fs')
|
||||||
|
const content = `import cron from 'node-cron'
|
||||||
|
import { prisma } from './db.js'
|
||||||
|
import mysql from 'mysql2/promise'
|
||||||
|
import { fetchNewsList, persistNews } from '../scripts/fetchers/news.fetcher.js'
|
||||||
|
import { checkAfterRefresh } from './alert-engine.js'
|
||||||
|
|
||||||
|
function parseUrl(url) {
|
||||||
|
const u = new URL(url)
|
||||||
|
return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\\//, '') }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openConn() {
|
||||||
|
return mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每天凌晨 3:00 (本地时间) 拉取新闻 + 跑跨源对比
|
||||||
|
function scheduleNews() {
|
||||||
|
cron.schedule('0 3 * * *', async () => {
|
||||||
|
console.log('[cron] 3:00 触发')
|
||||||
|
try {
|
||||||
|
const list = await fetchNewsList()
|
||||||
|
const r = await persistNews(list)
|
||||||
|
console.log('[cron] 新闻入库: 新增 ' + r.inserted + ' / 跳过 ' + r.skipped)
|
||||||
|
const conn = await openConn()
|
||||||
|
try {
|
||||||
|
const a = await checkAfterRefresh(conn)
|
||||||
|
console.log('[cron] 报警检测: 新增 ' + a.inserted + ' 条 (扫描 ' + a.scanned + ')')
|
||||||
|
} finally { await conn.end() }
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[cron] 任务失败:', e.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log('[cron] 已注册: 每天 03:00 拉取新闻 + 跨源报警检测')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每 2 分钟: 拉取 komari 节点 + 服务自检
|
||||||
|
function scheduleMonitors() {
|
||||||
|
cron.schedule('*/2 * * * *', async () => {
|
||||||
|
try {
|
||||||
|
const k = require('./komari-monitor.cjs')
|
||||||
|
const r = await k.runOnce()
|
||||||
|
console.log('[cron] komari: nodes=' + r.nodes + ' snapshots=' + r.snapshots + ' history=' + r.history + ' failed=' + r.failed)
|
||||||
|
} catch (e) { console.error('[cron] komari 任务失败:', e.message) }
|
||||||
|
try {
|
||||||
|
const h = require('./health-check.cjs')
|
||||||
|
const arr = await h.runOnce()
|
||||||
|
const down = arr.filter(x => x.status !== 'up').map(x => x.name)
|
||||||
|
console.log('[cron] health: ' + arr.length + ' 项, down=' + (down.length ? down.join(',') : '无'))
|
||||||
|
} catch (e) { console.error('[cron] health 任务失败:', e.message) }
|
||||||
|
})
|
||||||
|
console.log('[cron] 已注册: 每 2 分钟 komari + 服务自检')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每小时: 清理 24h 前的 komari 历史
|
||||||
|
function scheduleMonitorCleanup() {
|
||||||
|
cron.schedule('0 * * * *', async () => {
|
||||||
|
try {
|
||||||
|
const k = require('./komari-monitor.cjs')
|
||||||
|
const n = await k.cleanupOld()
|
||||||
|
console.log('[cron] 监控历史清理: 删除 ' + n + ' 条')
|
||||||
|
} catch (e) { console.error('[cron] 监控清理失败:', e.message) }
|
||||||
|
})
|
||||||
|
console.log('[cron] 已注册: 每小时清理 24h 前监控历史')
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleNews()
|
||||||
|
scheduleMonitors()
|
||||||
|
scheduleMonitorCleanup()
|
||||||
|
`
|
||||||
|
fs.writeFileSync('server/cron.js', content, 'utf8')
|
||||||
|
console.log('OK len=' + content.length)
|
||||||
@ -50,15 +50,17 @@ function parseNewsSheet(sheet, impactBase = 0) {
|
|||||||
const out = []
|
const out = []
|
||||||
for (let i = 0; i < sheet.items.length; i++) {
|
for (let i = 0; i < sheet.items.length; i++) {
|
||||||
const r = sheet.items[i]
|
const r = sheet.items[i]
|
||||||
const title = String(r[0] || '').trim()
|
const title = String(r[0] || '').trim().slice(0, 190)
|
||||||
if (!title) continue
|
if (!title) continue
|
||||||
const publishedAt = parsePublishedAt(r[2])
|
const publishedAt = parsePublishedAt(r[2])
|
||||||
if (!publishedAt) continue
|
if (!publishedAt) continue
|
||||||
|
const body = String(r[1] || '').trim()
|
||||||
out.push({
|
out.push({
|
||||||
title,
|
title,
|
||||||
summary: String(r[1] || '').trim().slice(0, 180),
|
summary: body.slice(0, 180),
|
||||||
|
content: body.slice(0, 8000), // 正文全文 (MCP "摘要"列实际是正文), 上限防异常超长
|
||||||
source: String(r[3] || '').trim().slice(0, 80),
|
source: String(r[3] || '').trim().slice(0, 80),
|
||||||
url: String(r[4] || '').trim().slice(0, 400),
|
url: String(r[4] || '').trim().slice(0, 190), // 列宽 varchar(191), 超长会被 MySQL 拒绝
|
||||||
publishedAt,
|
publishedAt,
|
||||||
impactRank: impactBase + i + 1,
|
impactRank: impactBase + i + 1,
|
||||||
})
|
})
|
||||||
@ -103,20 +105,27 @@ export async function persistNews(newsList) {
|
|||||||
if (!process.env.DATABASE_URL) throw new Error('缺少 DATABASE_URL')
|
if (!process.env.DATABASE_URL) throw new Error('缺少 DATABASE_URL')
|
||||||
if (!newsList || newsList.length === 0) return { inserted: 0, skipped: 0 }
|
if (!newsList || newsList.length === 0) return { inserted: 0, skipped: 0 }
|
||||||
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
let inserted = 0, skipped = 0
|
let inserted = 0, skipped = 0, failed = 0
|
||||||
try {
|
try {
|
||||||
for (const n of newsList) {
|
for (const n of newsList) {
|
||||||
const [r] = await conn.execute(
|
try {
|
||||||
'INSERT INTO `News` (`title`, `summary`, `source`, `url`, `publishedAt`, `impactRank`, `createdAt`) ' +
|
// 注意: content 列依赖迁移 add_news_content 已应用, 发布顺序须先迁移后跑本脚本
|
||||||
'VALUES (?, ?, ?, ?, ?, ?, NOW(3)) ' +
|
// 重复命中 (title, publishedAt) 时只回填仍为 NULL 的 content, 避免覆盖已有全文
|
||||||
'ON DUPLICATE KEY UPDATE `impactRank` = VALUES(`impactRank`)',
|
const [r] = await conn.execute(
|
||||||
[n.title, n.summary, n.source, n.url, n.publishedAt, n.impactRank],
|
'INSERT INTO `News` (`title`, `summary`, `content`, `source`, `url`, `publishedAt`, `impactRank`, `createdAt`) ' +
|
||||||
)
|
'VALUES (?, ?, ?, ?, ?, ?, ?, NOW(3)) ' +
|
||||||
if (r.affectedRows === 1) inserted++
|
'AS new ON DUPLICATE KEY UPDATE `impactRank` = new.`impactRank`, `content` = IFNULL(News.`content`, new.`content`)',
|
||||||
else skipped++
|
[n.title, n.summary, n.content || null, n.source, n.url, n.publishedAt, n.impactRank],
|
||||||
|
)
|
||||||
|
if (r.affectedRows === 1) inserted++
|
||||||
|
else skipped++
|
||||||
|
} catch (e) {
|
||||||
|
failed++
|
||||||
|
console.error('[News] 单条入库失败:', n.title, e.message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
await conn.end()
|
await conn.end()
|
||||||
}
|
}
|
||||||
return { inserted, skipped }
|
return { inserted, skipped, failed }
|
||||||
}
|
}
|
||||||
@ -7,6 +7,6 @@ async function main() {
|
|||||||
console.log('[News] 拉取 ' + list.length + ' 条, 耗时 ' + ((Date.now() - t0) / 1000).toFixed(1) + 's')
|
console.log('[News] 拉取 ' + list.length + ' 条, 耗时 ' + ((Date.now() - t0) / 1000).toFixed(1) + 's')
|
||||||
if (list.length === 0) { console.log('[News] 无数据, 跳过'); return }
|
if (list.length === 0) { console.log('[News] 无数据, 跳过'); return }
|
||||||
const r = await mod.persistNews(list)
|
const r = await mod.persistNews(list)
|
||||||
console.log('[News] 入库: 新增 ' + r.inserted + ' 条, 重复跳过 ' + r.skipped + ' 条')
|
console.log('[News] 入库: 新增 ' + r.inserted + ' 条, 重复跳过 ' + r.skipped + ' 条' + (r.failed ? ', 失败 ' + r.failed + ' 条' : ''))
|
||||||
}
|
}
|
||||||
main().catch((e) => { console.error('[News] 失败:', e); process.exit(1) })
|
main().catch((e) => { console.error('[News] 失败:', e); process.exit(1) })
|
||||||
|
|||||||
@ -33,4 +33,36 @@ function scheduleNews() {
|
|||||||
console.log('[cron] 已注册: 每天 03:00 拉取新闻 + 跨源报警检测')
|
console.log('[cron] 已注册: 每天 03:00 拉取新闻 + 跨源报警检测')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 每 2 分钟: 拉取 komari 节点 + 服务自检
|
||||||
|
function scheduleMonitors() {
|
||||||
|
cron.schedule('*/2 * * * *', async () => {
|
||||||
|
try {
|
||||||
|
const k = require('./komari-monitor.cjs')
|
||||||
|
const r = await k.runOnce()
|
||||||
|
console.log('[cron] komari: nodes=' + r.nodes + ' snapshots=' + r.snapshots + ' history=' + r.history + ' failed=' + r.failed)
|
||||||
|
} catch (e) { console.error('[cron] komari 任务失败:', e.message) }
|
||||||
|
try {
|
||||||
|
const h = require('./health-check.cjs')
|
||||||
|
const arr = await h.runOnce()
|
||||||
|
const down = arr.filter(x => x.status !== 'up').map(x => x.name)
|
||||||
|
console.log('[cron] health: ' + arr.length + ' 项, down=' + (down.length ? down.join(',') : '无'))
|
||||||
|
} catch (e) { console.error('[cron] health 任务失败:', e.message) }
|
||||||
|
})
|
||||||
|
console.log('[cron] 已注册: 每 2 分钟 komari + 服务自检')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每小时: 清理 24h 前的 komari 历史
|
||||||
|
function scheduleMonitorCleanup() {
|
||||||
|
cron.schedule('0 * * * *', async () => {
|
||||||
|
try {
|
||||||
|
const k = require('./komari-monitor.cjs')
|
||||||
|
const n = await k.cleanupOld()
|
||||||
|
console.log('[cron] 监控历史清理: 删除 ' + n + ' 条')
|
||||||
|
} catch (e) { console.error('[cron] 监控清理失败:', e.message) }
|
||||||
|
})
|
||||||
|
console.log('[cron] 已注册: 每小时清理 24h 前监控历史')
|
||||||
|
}
|
||||||
|
|
||||||
scheduleNews()
|
scheduleNews()
|
||||||
|
scheduleMonitors()
|
||||||
|
scheduleMonitorCleanup()
|
||||||
|
|||||||
55
server/health-check.cjs
Normal file
55
server/health-check.cjs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
// 服务健康检查: 探测本应用各关键端点, 写入 ServiceCheck 表
|
||||||
|
const mysql = require('mysql2/promise')
|
||||||
|
|
||||||
|
function parseUrl(url) { const u = new URL(url); return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\//, '') } }
|
||||||
|
|
||||||
|
const TARGETS = [
|
||||||
|
{ name: 'api-health', url: 'http://127.0.0.1:' + (process.env.PORT || 3001) + '/api/health' },
|
||||||
|
{ name: 'auth-login', url: 'http://127.0.0.1:' + (process.env.PORT || 3001) + '/api/auth/login' },
|
||||||
|
]
|
||||||
|
|
||||||
|
async function probeOne(target) {
|
||||||
|
const t0 = Date.now()
|
||||||
|
try {
|
||||||
|
const ctrl = new AbortController()
|
||||||
|
setTimeout(() => ctrl.abort(), 8000)
|
||||||
|
const r = await fetch(target.url, { method: 'GET', signal: ctrl.signal })
|
||||||
|
const latency = Date.now() - t0
|
||||||
|
const status = (r.ok || r.status === 400 || r.status === 401) ? 'up' : 'down'
|
||||||
|
const msg = 'HTTP ' + r.status
|
||||||
|
return { name: target.name, url: target.url, status, latencyMs: latency, message: msg }
|
||||||
|
} catch (e) {
|
||||||
|
return { name: target.name, url: target.url, status: 'down', latencyMs: Date.now() - t0, message: e.message || String(e) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkDb() {
|
||||||
|
const t0 = Date.now()
|
||||||
|
try {
|
||||||
|
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
|
await conn.execute('SELECT 1')
|
||||||
|
await conn.end()
|
||||||
|
return { name: 'database', url: 'mysql://' + (new URL(process.env.DATABASE_URL)).host, status: 'up', latencyMs: Date.now() - t0, message: 'ok' }
|
||||||
|
} catch (e) {
|
||||||
|
return { name: 'database', url: 'mysql', status: 'down', latencyMs: Date.now() - t0, message: e.message }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runOnce() {
|
||||||
|
const results = await Promise.all([...TARGETS.map(probeOne), checkDb()])
|
||||||
|
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
|
try {
|
||||||
|
for (const r of results) {
|
||||||
|
await conn.execute(
|
||||||
|
'INSERT INTO `ServiceCheck` (`name`,`url`,`status`,`latencyMs`,`message`,`lastCheckAt`,`createdAt`,`updatedAt`) VALUES (?,?,?,?,?,NOW(3),NOW(3),NOW(3)) ' +
|
||||||
|
'ON DUPLICATE KEY UPDATE `url`=VALUES(`url`),`status`=VALUES(`status`),`latencyMs`=VALUES(`latencyMs`),`message`=VALUES(`message`),`lastCheckAt`=NOW(3),`updatedAt`=NOW(3)',
|
||||||
|
[r.name, r.url, r.status, r.latencyMs, r.message.slice(0, 180)],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await conn.end()
|
||||||
|
}
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { runOnce, TARGETS }
|
||||||
120
server/komari-monitor.cjs
Normal file
120
server/komari-monitor.cjs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// komari 服务器监控拉取: 公开 API (无需 key)
|
||||||
|
// GET /api/nodes 节点列表
|
||||||
|
// GET /api/recent/{uuid} 最近实时状态 (数组, 多个时间点)
|
||||||
|
// GET /api/records/load?uuid=... 1 分钟粒度历史 (保留 24h)
|
||||||
|
const mysql = require('mysql2/promise')
|
||||||
|
|
||||||
|
const BASE = process.env.KOMARI_BASE || 'https://komari.honor3.com'
|
||||||
|
const TIMEOUT = 20000
|
||||||
|
|
||||||
|
function parseUrl(url) {
|
||||||
|
const u = new URL(url)
|
||||||
|
return { host: u.hostname, port: Number(u.port) || 3306, user: decodeURIComponent(u.username), password: decodeURIComponent(u.password), database: u.pathname.replace(/^\//, '') }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jget(path) {
|
||||||
|
const ctrl = new AbortController()
|
||||||
|
setTimeout(() => ctrl.abort(), TIMEOUT)
|
||||||
|
const r = await fetch(BASE + path, { headers: { 'User-Agent': 'iboard-monitor/1.0' }, signal: ctrl.signal })
|
||||||
|
if (!r.ok) throw new Error('komari ' + path + ' HTTP ' + r.status)
|
||||||
|
const j = await r.json()
|
||||||
|
if (j.status && j.status !== 'success') throw new Error('komari ' + path + ' status: ' + j.status + ' ' + (j.message || ''))
|
||||||
|
return j.data
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拉一次所有节点 + 最近状态 + 1 分钟历史, 入库
|
||||||
|
async function runOnce() {
|
||||||
|
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
|
const stats = { nodes: 0, snapshots: 0, history: 0, failed: 0 }
|
||||||
|
try {
|
||||||
|
const nodes = await jget('/api/nodes')
|
||||||
|
if (!Array.isArray(nodes)) throw new Error('nodes 格式异常')
|
||||||
|
stats.nodes = nodes.length
|
||||||
|
for (const n of nodes) {
|
||||||
|
const uuid = n.uuid
|
||||||
|
if (!uuid) continue
|
||||||
|
try {
|
||||||
|
const recent = await jget('/api/recent/' + uuid)
|
||||||
|
let snap = null
|
||||||
|
if (Array.isArray(recent) && recent.length) snap = recent[recent.length - 1]
|
||||||
|
const recordedAt = snap ? new Date(snap.updated_at) : new Date()
|
||||||
|
const ramTotal = snap && snap.ram ? Number(snap.ram.total) : Number(n.mem_total || 0)
|
||||||
|
const diskTotal = snap && snap.disk ? Number(snap.disk.total) : Number(n.disk_total || 0)
|
||||||
|
await conn.execute(
|
||||||
|
'INSERT INTO `ServerNode` (`uuid`,`name`,`cpuName`,`os`,`region`,`memTotal`,`diskTotal`,`cpuCores`,' +
|
||||||
|
'`cpuUsage`,`ramUsed`,`swapUsed`,`load1`,`load5`,`load15`,`diskUsed`,`netIn`,`netOut`,`uptime`,`process`,`connections`,`status`,`recordedAt`,`createdAt`,`updatedAt`) ' +
|
||||||
|
'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,NOW(3),NOW(3)) ' +
|
||||||
|
'ON DUPLICATE KEY UPDATE `name`=VALUES(`name`),`cpuName`=VALUES(`cpuName`),`os`=VALUES(`os`),`region`=VALUES(`region`),' +
|
||||||
|
'`memTotal`=VALUES(`memTotal`),`diskTotal`=VALUES(`diskTotal`),`cpuCores`=VALUES(`cpuCores`),' +
|
||||||
|
'`cpuUsage`=VALUES(`cpuUsage`),`ramUsed`=VALUES(`ramUsed`),`swapUsed`=VALUES(`swapUsed`),' +
|
||||||
|
'`load1`=VALUES(`load1`),`load5`=VALUES(`load5`),`load15`=VALUES(`load15`),`diskUsed`=VALUES(`diskUsed`),' +
|
||||||
|
'`netIn`=VALUES(`netIn`),`netOut`=VALUES(`netOut`),`uptime`=VALUES(`uptime`),' +
|
||||||
|
'`process`=VALUES(`process`),`connections`=VALUES(`connections`),`status`=VALUES(`status`),' +
|
||||||
|
'`recordedAt`=VALUES(`recordedAt`),`updatedAt`=NOW(3)',
|
||||||
|
[
|
||||||
|
uuid, n.name || '', n.cpu_name || '', n.os || '', n.region || '',
|
||||||
|
ramTotal, diskTotal, Number(n.cpu_cores || 0),
|
||||||
|
snap ? Number(snap.cpu?.usage || 0) : 0,
|
||||||
|
snap ? Number(snap.ram?.used || 0) : 0,
|
||||||
|
snap ? Number(snap.swap?.used || 0) : 0,
|
||||||
|
snap ? Number(snap.load?.load1 || 0) : 0,
|
||||||
|
snap ? Number(snap.load?.load5 || 0) : 0,
|
||||||
|
snap ? Number(snap.load?.load15 || 0) : 0,
|
||||||
|
snap ? Number(snap.disk?.used || 0) : 0,
|
||||||
|
snap ? Number(snap.network?.down || 0) : 0,
|
||||||
|
snap ? Number(snap.network?.up || 0) : 0,
|
||||||
|
snap ? Number(snap.uptime || 0) : 0,
|
||||||
|
snap ? Number(snap.process || 0) : 0,
|
||||||
|
snap ? Number(snap.connections?.tcp || 0) : 0,
|
||||||
|
snap ? 'online' : 'unknown',
|
||||||
|
recordedAt,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
stats.snapshots++
|
||||||
|
|
||||||
|
let rec = null
|
||||||
|
try { rec = await jget('/api/records/load?uuid=' + uuid) } catch (e) { /* 节点未开启 record */ }
|
||||||
|
if (rec && Array.isArray(rec.records)) {
|
||||||
|
for (const r of rec.records) {
|
||||||
|
const period = new Date(r.time)
|
||||||
|
if (isNaN(period.getTime())) continue
|
||||||
|
const [res] = await conn.execute(
|
||||||
|
'INSERT INTO `ServerNodeHistory` (`uuid`,`period`,`cpu`,`ram`,`load`,`disk`,`netIn`,`netOut`,`process`,`connections`,`createdAt`) ' +
|
||||||
|
'VALUES (?,?,?,?,?,?,?,?,?,?,NOW(3)) ' +
|
||||||
|
'ON DUPLICATE KEY UPDATE `cpu`=VALUES(`cpu`),`ram`=VALUES(`ram`),`load`=VALUES(`load`),`disk`=VALUES(`disk`),' +
|
||||||
|
'`netIn`=VALUES(`netIn`),`netOut`=VALUES(`netOut`),`process`=VALUES(`process`),`connections`=VALUES(`connections`)',
|
||||||
|
[
|
||||||
|
uuid, period,
|
||||||
|
Number(r.cpu || 0), Number(r.ram || 0), Number(r.load || 0),
|
||||||
|
Number(r.disk || 0), Number(r.net_in || 0), Number(r.net_out || 0),
|
||||||
|
Number(r.process || 0), Number(r.connections || 0),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
if (res.affectedRows === 1) stats.history++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
stats.failed++
|
||||||
|
console.error('[komari] 节点 ' + uuid + ' 失败:', e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await conn.end()
|
||||||
|
}
|
||||||
|
return stats
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理 24h 之前的历史
|
||||||
|
async function cleanupOld() {
|
||||||
|
const conn = await mysql.createConnection(parseUrl(process.env.DATABASE_URL))
|
||||||
|
try {
|
||||||
|
const cutoff = new Date(Date.now() - 24 * 60 * 60 * 1000)
|
||||||
|
const [r] = await conn.execute('DELETE FROM `ServerNodeHistory` WHERE `period` < ?', [cutoff])
|
||||||
|
return r.affectedRows
|
||||||
|
} finally {
|
||||||
|
await conn.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { runOnce, cleanupOld, BASE }
|
||||||
|
|
||||||
@ -8,16 +8,21 @@ router.use(authRequired)
|
|||||||
// 报警列表: 按时间最新优先, cursor 分页 (cursor = id)
|
// 报警列表: 按时间最新优先, cursor 分页 (cursor = id)
|
||||||
router.get('/alerts', async (req, res, next) => {
|
router.get('/alerts', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const limit = Math.min(Number(req.query.limit) || 10, 50)
|
const rawLimit = Number(req.query.limit)
|
||||||
const cursor = req.query.cursor ? Number(req.query.cursor) : null
|
const limit = Number.isFinite(rawLimit) && rawLimit >= 1 ? Math.min(Math.floor(rawLimit), 50) : 10
|
||||||
|
const rawCursor = Number(req.query.cursor)
|
||||||
|
const cursor = Number.isInteger(rawCursor) && rawCursor > 0 ? rawCursor : null
|
||||||
const where = cursor ? { id: { lt: cursor } } : {}
|
const where = cursor ? { id: { lt: cursor } } : {}
|
||||||
const items = await prisma.alert.findMany({
|
const [items, total] = await Promise.all([
|
||||||
where,
|
prisma.alert.findMany({
|
||||||
orderBy: { id: 'desc' },
|
where,
|
||||||
take: limit,
|
orderBy: { id: 'desc' },
|
||||||
})
|
take: limit,
|
||||||
|
}),
|
||||||
|
prisma.alert.count(),
|
||||||
|
])
|
||||||
const nextCursor = items.length === limit ? String(items[items.length - 1].id) : null
|
const nextCursor = items.length === limit ? String(items[items.length - 1].id) : null
|
||||||
res.json({ items, nextCursor })
|
res.json({ items, nextCursor, total })
|
||||||
} catch (e) { next(e) }
|
} catch (e) { next(e) }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
42
server/routes/monitors.js
Normal file
42
server/routes/monitors.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Router } from 'express'
|
||||||
|
import { prisma } from '../db.js'
|
||||||
|
import { authRequired } from '../middleware.js'
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
router.use(authRequired)
|
||||||
|
|
||||||
|
// 服务器节点列表: 元数据 + 最新快照
|
||||||
|
router.get('/servers', async (_req, res, next) => {
|
||||||
|
try {
|
||||||
|
const rows = await prisma.serverNode.findMany({
|
||||||
|
orderBy: { name: 'asc' },
|
||||||
|
})
|
||||||
|
res.json({ items: rows })
|
||||||
|
} catch (e) { next(e) }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 节点时序: ?uuid=...&hours=24
|
||||||
|
router.get('/servers/:uuid/history', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const hours = Math.min(Math.max(Number(req.query.hours) || 24, 1), 168)
|
||||||
|
const since = new Date(Date.now() - hours * 3600 * 1000)
|
||||||
|
const rows = await prisma.serverNodeHistory.findMany({
|
||||||
|
where: { uuid: req.params.uuid, period: { gte: since } },
|
||||||
|
orderBy: { period: 'asc' },
|
||||||
|
take: 2000,
|
||||||
|
})
|
||||||
|
res.json({ items: rows })
|
||||||
|
} catch (e) { next(e) }
|
||||||
|
})
|
||||||
|
|
||||||
|
// 服务健康列表
|
||||||
|
router.get('/services', async (_req, res, next) => {
|
||||||
|
try {
|
||||||
|
const rows = await prisma.serviceCheck.findMany({
|
||||||
|
orderBy: { name: 'asc' },
|
||||||
|
})
|
||||||
|
res.json({ items: rows })
|
||||||
|
} catch (e) { next(e) }
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
@ -5,21 +5,47 @@ import { authRequired } from '../middleware.js'
|
|||||||
const router = Router()
|
const router = Router()
|
||||||
router.use(authRequired)
|
router.use(authRequired)
|
||||||
|
|
||||||
// 新闻列表: 按时间最新优先, cursor 分页 (cursor = publishedAt ISO 字符串)
|
// 新闻列表: 按时间最新优先, cursor 分页
|
||||||
// 用 prisma cursor 配合 id 兜底 (同一秒发布的新闻)
|
// cursor 格式: "<publishedAt ISO>|<lastId>", 兼容旧版纯时间戳 (不带 |)
|
||||||
|
// 同秒多条时用 id 兜底, 避免翻页时整批漏掉
|
||||||
router.get('/news', async (req, res, next) => {
|
router.get('/news', async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const limit = Math.min(Number(req.query.limit) || 10, 50)
|
const rawLimit = Number(req.query.limit)
|
||||||
const cursor = req.query.cursor || null
|
const limit = Number.isFinite(rawLimit) && rawLimit >= 1 ? Math.min(Math.floor(rawLimit), 50) : 10
|
||||||
const where = cursor ? { publishedAt: { lt: new Date(cursor) } } : {}
|
let where = {}
|
||||||
|
const cursor = req.query.cursor || ''
|
||||||
|
if (cursor) {
|
||||||
|
const [ts, idRaw] = cursor.split('|')
|
||||||
|
const c = new Date(ts)
|
||||||
|
if (!isNaN(c.getTime())) {
|
||||||
|
const id = Number(idRaw)
|
||||||
|
where = id > 0
|
||||||
|
? { OR: [{ publishedAt: { lt: c } }, { publishedAt: c, id: { lt: id } }] }
|
||||||
|
: { publishedAt: { lt: c } }
|
||||||
|
}
|
||||||
|
}
|
||||||
const items = await prisma.news.findMany({
|
const items = await prisma.news.findMany({
|
||||||
|
select: { id: true, title: true, summary: true, source: true, url: true, publishedAt: true, impactRank: true },
|
||||||
where,
|
where,
|
||||||
orderBy: [{ publishedAt: 'desc' }, { id: 'desc' }],
|
orderBy: [{ publishedAt: 'desc' }, { id: 'desc' }],
|
||||||
take: limit,
|
take: limit,
|
||||||
})
|
})
|
||||||
const nextCursor = items.length === limit ? items[items.length - 1].publishedAt.toISOString() : null
|
const nextCursor = items.length === limit && items.length > 0
|
||||||
|
? items[items.length - 1].publishedAt.toISOString() + '|' + items[items.length - 1].id
|
||||||
|
: null
|
||||||
res.json({ items, nextCursor })
|
res.json({ items, nextCursor })
|
||||||
} catch (e) { next(e) }
|
} catch (e) { next(e) }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 新闻详情: 含正文 content
|
||||||
|
router.get('/news/:id', async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const id = Number(req.params.id)
|
||||||
|
if (!Number.isInteger(id) || id <= 0) return res.status(400).json({ error: '无效的新闻 id' })
|
||||||
|
const item = await prisma.news.findUnique({ where: { id } })
|
||||||
|
if (!item) return res.status(404).json({ error: '新闻不存在' })
|
||||||
|
res.json({ item })
|
||||||
|
} catch (e) { next(e) }
|
||||||
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Routes, Route, Navigate, useLocation } from 'react-router-dom'
|
|||||||
import MobileShell from './layouts/MobileShell.jsx'
|
import MobileShell from './layouts/MobileShell.jsx'
|
||||||
import LoginPage from './pages/LoginPage.jsx'
|
import LoginPage from './pages/LoginPage.jsx'
|
||||||
import HomePage from './pages/HomePage.jsx'
|
import HomePage from './pages/HomePage.jsx'
|
||||||
|
import NewsDetail from './pages/news/NewsDetail.jsx'
|
||||||
import MacroPage from './pages/MacroPage.jsx'
|
import MacroPage from './pages/MacroPage.jsx'
|
||||||
import MonitorPage from './pages/MonitorPage.jsx'
|
import MonitorPage from './pages/MonitorPage.jsx'
|
||||||
import MePage from './pages/MePage.jsx'
|
import MePage from './pages/MePage.jsx'
|
||||||
@ -30,6 +31,7 @@ export default function App() {
|
|||||||
<Route path="/login" element={<RedirectIfAuthed><LoginPage /></RedirectIfAuthed>} />
|
<Route path="/login" element={<RedirectIfAuthed><LoginPage /></RedirectIfAuthed>} />
|
||||||
<Route element={<RequireAuth><MobileShell /></RequireAuth>}>
|
<Route element={<RequireAuth><MobileShell /></RequireAuth>}>
|
||||||
<Route path="/" element={<HomePage />} />
|
<Route path="/" element={<HomePage />} />
|
||||||
|
<Route path="/news/:id" element={<NewsDetail />} />
|
||||||
<Route path="/macro/*" element={<MacroPage />} />
|
<Route path="/macro/*" element={<MacroPage />} />
|
||||||
<Route path="/monitor" element={<MonitorPage />} />
|
<Route path="/monitor" element={<MonitorPage />} />
|
||||||
<Route path="/me" element={<MePage />} />
|
<Route path="/me" element={<MePage />} />
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { useEffect, useState, useRef, useCallback } from 'react'
|
import { useEffect, useState, useRef, useCallback } from 'react'
|
||||||
import { NavBar, List, Card, Empty, DotLoading } from 'antd-mobile'
|
import { useNavigate } from 'react-router-dom'
|
||||||
|
import { NavBar, Card, Empty, DotLoading, Badge } from 'antd-mobile'
|
||||||
import { api } from '../api/client.js'
|
import { api } from '../api/client.js'
|
||||||
|
import SwipeTabs from '../components/SwipeTabs.jsx'
|
||||||
|
|
||||||
// 首页: 报警模块 + 新闻模块
|
// 首页: 报警模块 + 新闻模块
|
||||||
// - 报警: World Bank 与妙想 MCP 跨源同 (指标, 周期) 数据差异超阈值 (按时间倒序)
|
// - 报警: World Bank 与妙想 MCP 跨源同 (指标, 周期) 数据差异超阈值 (按时间倒序)
|
||||||
@ -22,41 +24,62 @@ function fmtDate(s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function HomePage() {
|
export default function HomePage() {
|
||||||
|
const [active, setActive] = useState('news') // 默认选中新闻
|
||||||
|
const [alertCount, setAlertCount] = useState(0)
|
||||||
|
const handleAlertCount = useCallback((n) => setAlertCount(n), [])
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{
|
||||||
|
key: 'alerts',
|
||||||
|
label: (
|
||||||
|
<span className="tab-label">
|
||||||
|
报警
|
||||||
|
{alertCount > 0 && <Badge content={alertCount} />}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
render: () => <AlertSection onCount={handleAlertCount} />,
|
||||||
|
},
|
||||||
|
{ key: 'news', label: '新闻', render: () => <NewsSection /> },
|
||||||
|
]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-with-navbar">
|
<div className="page-with-navbar">
|
||||||
<NavBar back={null}>首页</NavBar>
|
<NavBar back={null}>首页</NavBar>
|
||||||
<div className="home-content">
|
<div className="home-content">
|
||||||
<AlertSection />
|
<SwipeTabs tabs={tabs} active={active} onChange={setActive} />
|
||||||
<NewsSection />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 报警模块
|
// 报警模块
|
||||||
function AlertSection() {
|
function AlertSection({ onCount }) {
|
||||||
const [items, setItems] = useState([])
|
const [items, setItems] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [done, setDone] = useState(false)
|
const [done, setDone] = useState(false)
|
||||||
const cursorRef = useRef(null)
|
const cursorRef = useRef(null)
|
||||||
const sentinelRef = useRef(null)
|
const sentinelRef = useRef(null)
|
||||||
|
const inFlightRef = useRef(false)
|
||||||
|
|
||||||
const fetchPage = useCallback(async () => {
|
const fetchPage = useCallback(async () => {
|
||||||
if (loading || done) return
|
if (inFlightRef.current || done) return
|
||||||
|
inFlightRef.current = true
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const url = '/alerts?limit=10' + (cursorRef.current ? '&cursor=' + cursorRef.current : '')
|
const url = '/alerts?limit=10' + (cursorRef.current ? '&cursor=' + cursorRef.current : '')
|
||||||
const data = await api(url)
|
const data = await api(url)
|
||||||
setItems((prev) => prev.concat(data.items || []))
|
setItems((prev) => prev.concat(data.items || []))
|
||||||
|
onCount?.(data.total ?? (data.items?.length || 0))
|
||||||
cursorRef.current = data.nextCursor
|
cursorRef.current = data.nextCursor
|
||||||
if (!data.nextCursor) setDone(true)
|
if (!data.nextCursor) setDone(true)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('alerts load error', e)
|
console.error('alerts load error', e)
|
||||||
setDone(true)
|
setDone(true)
|
||||||
} finally {
|
} finally {
|
||||||
|
inFlightRef.current = false
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [loading, done])
|
}, [done, onCount])
|
||||||
|
|
||||||
useEffect(() => { fetchPage() }, [])
|
useEffect(() => { fetchPage() }, [])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -106,9 +129,12 @@ function NewsSection() {
|
|||||||
const [done, setDone] = useState(false)
|
const [done, setDone] = useState(false)
|
||||||
const cursorRef = useRef(null)
|
const cursorRef = useRef(null)
|
||||||
const sentinelRef = useRef(null)
|
const sentinelRef = useRef(null)
|
||||||
|
const inFlightRef = useRef(false)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
const fetchPage = useCallback(async () => {
|
const fetchPage = useCallback(async () => {
|
||||||
if (loading || done) return
|
if (inFlightRef.current || done) return
|
||||||
|
inFlightRef.current = true
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const url = '/news?limit=10' + (cursorRef.current ? '&cursor=' + encodeURIComponent(cursorRef.current) : '')
|
const url = '/news?limit=10' + (cursorRef.current ? '&cursor=' + encodeURIComponent(cursorRef.current) : '')
|
||||||
@ -120,9 +146,10 @@ function NewsSection() {
|
|||||||
console.error('news load error', e)
|
console.error('news load error', e)
|
||||||
setDone(true)
|
setDone(true)
|
||||||
} finally {
|
} finally {
|
||||||
|
inFlightRef.current = false
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [loading, done])
|
}, [done])
|
||||||
|
|
||||||
useEffect(() => { fetchPage() }, [])
|
useEffect(() => { fetchPage() }, [])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -144,7 +171,7 @@ function NewsSection() {
|
|||||||
) : (
|
) : (
|
||||||
<div className="section-list">
|
<div className="section-list">
|
||||||
{items.map((n) => (
|
{items.map((n) => (
|
||||||
<Card key={n.id} className="news-card">
|
<Card key={n.id} className="news-card" onClick={() => navigate('/news/' + n.id)}>
|
||||||
<div className="news-title">{n.title}</div>
|
<div className="news-title">{n.title}</div>
|
||||||
{n.summary && <div className="news-summary">{n.summary}</div>}
|
{n.summary && <div className="news-summary">{n.summary}</div>}
|
||||||
<div className="news-meta">
|
<div className="news-meta">
|
||||||
|
|||||||
56
src/pages/news/NewsDetail.jsx
Normal file
56
src/pages/news/NewsDetail.jsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useParams, useNavigate } from 'react-router-dom'
|
||||||
|
import { NavBar } from 'antd-mobile'
|
||||||
|
import { api } from '../../api/client.js'
|
||||||
|
|
||||||
|
// 新闻详情: /news/:id
|
||||||
|
// 正文来自 News.content (MCP 返回的完整正文), 旧数据可能无 content, 兜底显示摘要
|
||||||
|
function fmtDate(s) {
|
||||||
|
if (!s) return ''
|
||||||
|
const d = new Date(s)
|
||||||
|
if (isNaN(d.getTime())) return s
|
||||||
|
const pad = (n) => String(n).padStart(2, '0')
|
||||||
|
return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NewsDetail() {
|
||||||
|
const { id } = useParams()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [item, setItem] = useState(null)
|
||||||
|
const [err, setErr] = useState('')
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false
|
||||||
|
setLoading(true)
|
||||||
|
api('/news/' + encodeURIComponent(id))
|
||||||
|
.then((data) => { if (!cancelled) setItem(data.item) })
|
||||||
|
.catch((e) => !cancelled && setErr(e.message))
|
||||||
|
.finally(() => !cancelled && setLoading(false))
|
||||||
|
return () => { cancelled = true }
|
||||||
|
}, [id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="article-page">
|
||||||
|
<NavBar onBack={() => navigate(-1)}>新闻详情</NavBar>
|
||||||
|
{loading && <div className="loading">加载中...</div>}
|
||||||
|
{err && <div className="error-banner">{err}</div>}
|
||||||
|
{!loading && !err && !item && <div className="empty">新闻不存在</div>}
|
||||||
|
{!loading && !err && item && (
|
||||||
|
<div className="news-detail">
|
||||||
|
<h1>{item.title}</h1>
|
||||||
|
<div className="meta">
|
||||||
|
{item.source || '未知来源'} · {fmtDate(item.publishedAt)} · #{item.impactRank}
|
||||||
|
{/^https?:\/\//i.test(item.url || '') && (
|
||||||
|
<a className="news-origin" href={item.url} target="_blank" rel="noreferrer">原文链接</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="news-content">
|
||||||
|
{item.content ? item.content : item.summary}
|
||||||
|
{!item.content && <div className="news-content-empty">(本条暂无全文,仅摘要)</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -197,6 +197,8 @@ input, textarea, select { font: inherit; }
|
|||||||
.me-logout { color: var(--color-danger); }
|
.me-logout { color: var(--color-danger); }
|
||||||
/* 首页模块: 报警 + 新闻 */
|
/* 首页模块: 报警 + 新闻 */
|
||||||
.home-content { padding: 0 0 16px; }
|
.home-content { padding: 0 0 16px; }
|
||||||
|
.home-content .adm-tabs-content { --content-padding: 0; }
|
||||||
|
.home-content .tab-label { display: inline-flex; align-items: center; gap: 4px; }
|
||||||
.home-section { padding: 0 12px 16px; }
|
.home-section { padding: 0 12px 16px; }
|
||||||
.section-title { font-size: 16px; font-weight: 700; padding: 14px 4px 8px; color: var(--color-text); }
|
.section-title { font-size: 16px; font-weight: 700; padding: 14px 4px 8px; color: var(--color-text); }
|
||||||
.section-list { display: flex; flex-direction: column; gap: 8px; }
|
.section-list { display: flex; flex-direction: column; gap: 8px; }
|
||||||
@ -214,4 +216,12 @@ input, textarea, select { font: inherit; }
|
|||||||
.news-summary { font-size: 12px; color: var(--color-muted); margin-top: 4px; line-height: 1.5; max-height: 3.6em; overflow: hidden; }
|
.news-summary { font-size: 12px; color: var(--color-muted); margin-top: 4px; line-height: 1.5; max-height: 3.6em; overflow: hidden; }
|
||||||
.news-meta { display: flex; gap: 10px; font-size: 11px; color: var(--color-muted); margin-top: 6px; }
|
.news-meta { display: flex; gap: 10px; font-size: 11px; color: var(--color-muted); margin-top: 6px; }
|
||||||
.news-rank { color: var(--color-primary); font-weight: 600; }
|
.news-rank { color: var(--color-primary); font-weight: 600; }
|
||||||
|
.news-card:active .adm-card-body { background: #f5f5f5; }
|
||||||
|
/* 新闻详情页 */
|
||||||
|
.news-detail { background: #fff; padding: 18px 16px 24px; }
|
||||||
|
.news-detail h1 { font-size: 20px; line-height: 1.35; margin-bottom: 8px; }
|
||||||
|
.news-detail .meta { font-size: 12px; color: var(--color-muted); margin-bottom: 14px; padding-bottom: 10px; border-bottom: 1px solid var(--color-border); display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
|
||||||
|
.news-origin { color: var(--color-primary); margin-left: auto; }
|
||||||
|
.news-content { font-size: 15px; line-height: 1.75; color: #333; white-space: pre-wrap; word-break: break-word; }
|
||||||
|
.news-content-empty { margin-top: 12px; font-size: 12px; color: var(--color-muted); }
|
||||||
.scroll-sentinel { text-align: center; padding: 14px 0 4px; color: var(--color-muted); font-size: 12px; }
|
.scroll-sentinel { text-align: center; padding: 14px 0 4px; color: var(--color-muted); font-size: 12px; }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user