1
1
mirror of https://github.com/ZeroCatDev/ClassworksKV.git synced 2026-02-04 07:44:40 +00:00

Compare commits

..

13 Commits
v1.3.9 ... main

Author SHA1 Message Date
Sunwuyuan
88d5062638
1.3.15 2026-02-01 21:15:54 +08:00
Sunwuyuan
2f7f78f657
fix: set default values for id fields in Account, AppInstall, AutoAuth models and create migration for Device sequence 2026-02-01 21:15:49 +08:00
Sunwuyuan
80e7214860
1.3.14 2026-02-01 20:51:15 +08:00
Sunwuyuan
e7f155b021
1.3.13 2026-02-01 20:08:29 +08:00
Sunwuyuan
8c8ebae6b7
迁移到postgresql(叹气) 2026-02-01 20:08:13 +08:00
Sunwuyuan
6e8ba54fd6
1.3.12 2026-02-01 19:10:07 +08:00
Sunwuyuan
dc5de6e63a
fix: standardize autoAuth field names to autoauth in apps and auto-auth routes 2026-02-01 19:09:59 +08:00
Sunwuyuan
c063dcf5b6
1.3.11 2026-02-01 19:00:19 +08:00
Sunwuyuan
29030f90bd
fix: standardize device-related field names and timestamps across routes and utils
- Updated field names from camelCase to lowercase (e.g., deviceId to deviceid, createdAt to createdat) in auto-auth, device-auth, device, kv-token, and other related files.
- Adjusted database migration scripts to set default timestamps with timezone for createdat and updatedat fields in relevant tables.
- Ensured consistency in handling device-related data across the application.
2026-02-01 18:59:58 +08:00
Sunwuyuan
ba08d2c478
1.3.10 2026-02-01 18:30:31 +08:00
Sunwuyuan
a5728ac2a5
Merge branch 'main' of https://github.com/ZeroCatDev/ClassworksKV 2026-02-01 18:30:27 +08:00
Sunwuyuan
ce3c8ee41f
迁移到postgresql 2026-02-01 18:30:23 +08:00
Sunwuyuan
e209bd92e2
api.zcservice.houlang.cloud 2026-01-17 18:15:29 +08:00
46 changed files with 11160 additions and 300 deletions

91
docker/schema.prisma Normal file
View File

@ -0,0 +1,91 @@
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
model KVStore {
deviceId Int
key String
value Json
creatorIp String? @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@id([deviceId, key])
}
model Account {
id String @id @default(cuid())
provider String // OAuth提供者 (例如: google, github, gitlab等)
providerId String // 提供者返回的用户唯一ID
email String? // 用户邮箱
name String? // 用户名称
avatarUrl String? // 用户头像URL
providerData Json? // OAuth提供者返回的完整信息
accessToken String? @db.Text // 账户访问令牌
refreshToken String? @db.Text // 刷新令牌
refreshTokenExpiry DateTime? // 刷新令牌过期时间
tokenVersion Int @default(1) // 令牌版本,用于令牌失效
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联的设备
devices Device[]
@@unique([provider, providerId]) // 确保同一提供者的用户ID唯一
}
model Device {
id Int @id @default(autoincrement())
uuid String @unique // 设备的唯一标识符
name String?
accountId String? // 关联的账户ID
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
password String?
passwordHint String?
namespace String? @unique // 用户自定义的唯一命名空间
// 关联关系
account Account? @relation(fields: [accountId], references: [id], onDelete: SetNull)
appInstalls AppInstall[]
kvStore KVStore[] // 设备相关的KV存储
autoAuths AutoAuth[] // 自动授权配置
}
model AppInstall {
id String @id @default(cuid())
deviceId Int // 关联的设备ID
appId String // 应用ID (SHA256 hash)
token String @unique // 应用安装的唯一访问令牌,拥有完整KV读写权限
note String? // 安装备注
isReadOnly Boolean @default(false) // 是否只读
deviceType String? // 设备类型: teacher(教师), student(学生), classroom(班级一体机), parent(家长)
installedAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
}
model AutoAuth {
id String @id @default(cuid())
deviceId Int // 关联的设备ID
password String? // 配置密码,可以为空
deviceType String? // 自动设备类型: teacher(教师), student(学生), classroom(班级一体机), parent(家长)
isReadOnly Boolean @default(false) // 是否只读
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@unique([deviceId, password]) // 同一设备的密码必须唯一
}

View File

@ -0,0 +1,44 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma-related types and utilities in a browser.
* Use it to get access to models, enums, and input types.
*
* This file does not contain a `PrismaClient` class, nor several other helpers that are intended as server-side only.
* See `client.ts` for the standard, server-side entry point.
*
* 🟢 You can import this file directly.
*/
import * as Prisma from './internal/prismaNamespaceBrowser.ts'
export { Prisma }
export * as $Enums from './enums.ts'
export * from './enums.ts';
/**
* Model Account
*
*/
export type Account = Prisma.AccountModel
/**
* Model AppInstall
*
*/
export type AppInstall = Prisma.AppInstallModel
/**
* Model AutoAuth
*
*/
export type AutoAuth = Prisma.AutoAuthModel
/**
* Model Device
*
*/
export type Device = Prisma.DeviceModel
/**
* Model KVStore
*
*/
export type KVStore = Prisma.KVStoreModel

View File

@ -0,0 +1,66 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
*
* 🟢 You can import this file directly.
*/
import * as process from 'node:process'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'
globalThis['__dirname'] = path.dirname(fileURLToPath(import.meta.url))
import * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.ts"
import * as $Class from "./internal/class.ts"
import * as Prisma from "./internal/prismaNamespace.ts"
export * as $Enums from './enums.ts'
export * from "./enums.ts"
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Accounts
* const accounts = await prisma.account.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export const PrismaClient = $Class.getPrismaClientClass()
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
export { Prisma }
/**
* Model Account
*
*/
export type Account = Prisma.AccountModel
/**
* Model AppInstall
*
*/
export type AppInstall = Prisma.AppInstallModel
/**
* Model AutoAuth
*
*/
export type AutoAuth = Prisma.AutoAuthModel
/**
* Model Device
*
*/
export type Device = Prisma.DeviceModel
/**
* Model KVStore
*
*/
export type KVStore = Prisma.KVStoreModel

View File

@ -0,0 +1,502 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports various common sort, input & filter types that are not directly linked to a particular model.
*
* 🟢 You can import this file directly.
*/
import type * as runtime from "@prisma/client/runtime/client"
import * as $Enums from "./enums.ts"
import type * as Prisma from "./internal/prismaNamespace.ts"
export type StringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
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>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type StringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | 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>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type JsonNullableFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonNullableFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonNullableFilterBase<$PrismaModel>>, 'path'>>
export type JsonNullableFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}
export type DateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type DateTimeNullableFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
}
export type IntFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntFilter<$PrismaModel> | number
}
export type SortOrderInput = {
sort: Prisma.SortOrder
nulls?: Prisma.NullsOrder
}
export type StringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
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>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | 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>
mode?: Prisma.QueryMode
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type JsonNullableWithAggregatesFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonNullableWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
export type JsonNullableWithAggregatesFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedJsonNullableFilter<$PrismaModel>
_max?: Prisma.NestedJsonNullableFilter<$PrismaModel>
}
export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
}
export type IntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedIntFilter<$PrismaModel>
_max?: Prisma.NestedIntFilter<$PrismaModel>
}
export type BoolFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
}
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedBoolFilter<$PrismaModel>
_max?: Prisma.NestedBoolFilter<$PrismaModel>
}
export type JsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonFilterBase<$PrismaModel>>, 'path'>>
export type JsonFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}
export type JsonWithAggregatesFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>,
Required<JsonWithAggregatesFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<JsonWithAggregatesFilterBase<$PrismaModel>>, 'path'>>
export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedJsonFilter<$PrismaModel>
_max?: Prisma.NestedJsonFilter<$PrismaModel>
}
export type NestedStringFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
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>
not?: Prisma.NestedStringFilter<$PrismaModel> | string
}
export type NestedStringNullableFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | 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>
not?: Prisma.NestedStringNullableFilter<$PrismaModel> | string | null
}
export type NestedDateTimeFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
}
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
}
export type NestedIntFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntFilter<$PrismaModel> | number
}
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
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>
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedStringFilter<$PrismaModel>
_max?: Prisma.NestedStringFilter<$PrismaModel>
}
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | 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>
not?: Prisma.NestedStringNullableWithAggregatesFilter<$PrismaModel> | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedStringNullableFilter<$PrismaModel>
_max?: Prisma.NestedStringNullableFilter<$PrismaModel>
}
export type NestedIntNullableFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel> | null
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel> | null
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntNullableFilter<$PrismaModel> | number | null
}
export type NestedJsonNullableFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<NestedJsonNullableFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>,
Required<NestedJsonNullableFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<NestedJsonNullableFilterBase<$PrismaModel>>, 'path'>>
export type NestedJsonNullableFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}
export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel>
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeWithAggregatesFilter<$PrismaModel> | Date | string
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
}
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
}
export type NestedIntWithAggregatesFilter<$PrismaModel = never> = {
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
lt?: number | Prisma.IntFieldRefInput<$PrismaModel>
lte?: number | Prisma.IntFieldRefInput<$PrismaModel>
gt?: number | Prisma.IntFieldRefInput<$PrismaModel>
gte?: number | Prisma.IntFieldRefInput<$PrismaModel>
not?: Prisma.NestedIntWithAggregatesFilter<$PrismaModel> | number
_count?: Prisma.NestedIntFilter<$PrismaModel>
_avg?: Prisma.NestedFloatFilter<$PrismaModel>
_sum?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedIntFilter<$PrismaModel>
_max?: Prisma.NestedIntFilter<$PrismaModel>
}
export type NestedFloatFilter<$PrismaModel = never> = {
equals?: number | Prisma.FloatFieldRefInput<$PrismaModel>
in?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
notIn?: number[] | Prisma.ListFloatFieldRefInput<$PrismaModel>
lt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
lte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gt?: number | Prisma.FloatFieldRefInput<$PrismaModel>
gte?: number | Prisma.FloatFieldRefInput<$PrismaModel>
not?: Prisma.NestedFloatFilter<$PrismaModel> | number
}
export type NestedBoolFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
}
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
_count?: Prisma.NestedIntFilter<$PrismaModel>
_min?: Prisma.NestedBoolFilter<$PrismaModel>
_max?: Prisma.NestedBoolFilter<$PrismaModel>
}
export type NestedJsonFilter<$PrismaModel = never> =
| Prisma.PatchUndefined<
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
Required<NestedJsonFilterBase<$PrismaModel>>
>
| Prisma.OptionalFlat<Omit<Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>
export type NestedJsonFilterBase<$PrismaModel = never> = {
equals?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
path?: string[]
mode?: Prisma.QueryMode | Prisma.EnumQueryModeFieldRefInput<$PrismaModel>
string_contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_starts_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
string_ends_with?: string | Prisma.StringFieldRefInput<$PrismaModel>
array_starts_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_ends_with?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
array_contains?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | null
lt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
lte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gt?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
gte?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel>
not?: runtime.InputJsonValue | Prisma.JsonFieldRefInput<$PrismaModel> | Prisma.JsonNullValueFilter
}

15
generated/prisma/enums.ts Normal file
View File

@ -0,0 +1,15 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This file exports all enum related types from the schema.
*
* 🟢 You can import this file directly.
*/
// This file is empty because there are no enums in the schema.
export {}

View File

@ -0,0 +1,232 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* Please import the `PrismaClient` class from the `client.ts` file instead.
*/
import * as runtime from "@prisma/client/runtime/client"
import type * as Prisma from "./prismaNamespace.ts"
const config: runtime.GetPrismaClientConfig = {
"previewFeatures": [],
"clientVersion": "7.3.0",
"engineVersion": "9d6ad21cbbceab97458517b147a6a09ff43aa735",
"activeProvider": "postgresql",
"inlineSchema": "generator client {\n provider = \"prisma-client\"\n output = \"../generated/prisma\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n}\n\nmodel Account {\n id String @id(map: \"idx_18303_PRIMARY\") @db.VarChar(191)\n provider String @db.VarChar(191)\n providerId String @db.VarChar(191)\n email String? @db.VarChar(191)\n name String? @db.VarChar(191)\n avatarUrl String? @db.VarChar(191)\n providerData Json? @db.Json\n accessToken String?\n createdAt DateTime @default(now()) @db.Timestamptz(6)\n updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)\n refreshToken String?\n refreshTokenExpiry DateTime? @db.Timestamptz(6)\n tokenVersion Int @default(1)\n\n devices Device[]\n\n @@unique([provider, providerId], map: \"idx_18303_Account_provider_providerId_key\")\n}\n\nmodel AppInstall {\n id String @id(map: \"idx_18310_PRIMARY\") @db.VarChar(191)\n deviceId Int\n appId String @db.VarChar(191)\n token String @unique(map: \"idx_18310_AppInstall_token_key\") @db.VarChar(191)\n note String? @db.VarChar(191)\n installedAt DateTime @default(now()) @db.Timestamptz(6)\n updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)\n deviceType String? @db.VarChar(191)\n isReadOnly Boolean @default(false)\n\n device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n\n @@index([deviceId], map: \"idx_18310_AppInstall_deviceId_fkey\")\n}\n\nmodel AutoAuth {\n id String @id(map: \"idx_18317_PRIMARY\") @db.VarChar(191)\n deviceId Int\n password String? @db.VarChar(191)\n deviceType String? @db.VarChar(191)\n isReadOnly Boolean @default(false)\n createdAt DateTime @default(now()) @db.Timestamptz(6)\n updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)\n\n device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n\n @@unique([deviceId, password], map: \"idx_18317_AutoAuth_deviceId_password_key\")\n}\n\nmodel Device {\n id Int @id(map: \"idx_18324_PRIMARY\")\n uuid String @unique(map: \"idx_18324_Device_uuid_key\") @db.VarChar(191)\n name String? @db.VarChar(191)\n accountId String? @db.VarChar(191)\n createdAt DateTime @default(now()) @db.Timestamptz(6)\n updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)\n password String? @db.VarChar(191)\n passwordHint String? @db.VarChar(191)\n namespace String? @unique(map: \"idx_18324_Device_namespace_key\") @db.VarChar(191)\n\n // 关联关系\n account Account? @relation(fields: [accountId], references: [id], onDelete: SetNull)\n appInstalls AppInstall[]\n kvStore KVStore[] // 设备相关的KV存储\n autoAuths AutoAuth[] // 自动授权配置\n\n @@index([accountId], map: \"idx_18324_Device_accountId_fkey\")\n}\n\nmodel KVStore {\n deviceId Int\n key String @db.VarChar(191)\n value Json @db.Json\n creatorIp String? @default(\"\") @db.VarChar(191)\n createdAt DateTime @default(now()) @db.Timestamptz(6)\n updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)\n\n device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)\n\n @@id([deviceId, key], map: \"idx_18330_PRIMARY\")\n}\n",
"runtimeDataModel": {
"models": {},
"enums": {},
"types": {}
}
}
config.runtimeDataModel = JSON.parse("{\"models\":{\"Account\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"provider\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"avatarUrl\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"providerData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"accessToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"refreshToken\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"refreshTokenExpiry\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"tokenVersion\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"devices\",\"kind\":\"object\",\"type\":\"Device\",\"relationName\":\"AccountToDevice\"}],\"dbName\":null},\"AppInstall\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"appId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"note\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"installedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"deviceType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isReadOnly\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"Device\",\"relationName\":\"AppInstallToDevice\"}],\"dbName\":null},\"AutoAuth\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"deviceType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"isReadOnly\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"Device\",\"relationName\":\"AutoAuthToDevice\"}],\"dbName\":null},\"Device\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"uuid\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"accountId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"password\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"passwordHint\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"namespace\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"account\",\"kind\":\"object\",\"type\":\"Account\",\"relationName\":\"AccountToDevice\"},{\"name\":\"appInstalls\",\"kind\":\"object\",\"type\":\"AppInstall\",\"relationName\":\"AppInstallToDevice\"},{\"name\":\"kvStore\",\"kind\":\"object\",\"type\":\"KVStore\",\"relationName\":\"DeviceToKVStore\"},{\"name\":\"autoAuths\",\"kind\":\"object\",\"type\":\"AutoAuth\",\"relationName\":\"AutoAuthToDevice\"}],\"dbName\":null},\"KVStore\":{\"fields\":[{\"name\":\"deviceId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"key\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"creatorIp\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"device\",\"kind\":\"object\",\"type\":\"Device\",\"relationName\":\"DeviceToKVStore\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
async function decodeBase64AsWasm(wasmBase64: string): Promise<WebAssembly.Module> {
const { Buffer } = await import('node:buffer')
const wasmArray = Buffer.from(wasmBase64, 'base64')
return new WebAssembly.Module(wasmArray)
}
config.compilerWasm = {
getRuntime: async () => await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.mjs"),
getQueryCompilerWasmModule: async () => {
const { wasm } = await import("@prisma/client/runtime/query_compiler_fast_bg.postgresql.wasm-base64.mjs")
return await decodeBase64AsWasm(wasm)
},
importName: "./query_compiler_fast_bg.js"
}
export type LogOptions<ClientOptions extends Prisma.PrismaClientOptions> =
'log' extends keyof ClientOptions ? ClientOptions['log'] extends Array<Prisma.LogLevel | Prisma.LogDefinition> ? Prisma.GetEvents<ClientOptions['log']> : never : never
export interface PrismaClientConstructor {
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Accounts
* const accounts = await prisma.account.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
new <
Options extends Prisma.PrismaClientOptions = Prisma.PrismaClientOptions,
LogOpts extends LogOptions<Options> = LogOptions<Options>,
OmitOpts extends Prisma.PrismaClientOptions['omit'] = Options extends { omit: infer U } ? U : Prisma.PrismaClientOptions['omit'],
ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
>(options: Prisma.Subset<Options, Prisma.PrismaClientOptions> ): PrismaClient<LogOpts, OmitOpts, ExtArgs>
}
/**
* ## Prisma Client
*
* Type-safe database client for TypeScript
* @example
* ```
* const prisma = new PrismaClient()
* // Fetch zero or more Accounts
* const accounts = await prisma.account.findMany()
* ```
*
* Read more in our [docs](https://pris.ly/d/client).
*/
export interface PrismaClient<
in LogOpts extends Prisma.LogLevel = never,
in out OmitOpts extends Prisma.PrismaClientOptions['omit'] = undefined,
in out ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs
> {
[K: symbol]: { types: Prisma.TypeMap<ExtArgs>['other'] }
$on<V extends LogOpts>(eventType: V, callback: (event: V extends 'query' ? Prisma.QueryEvent : Prisma.LogEvent) => void): PrismaClient;
/**
* Connect with the database
*/
$connect(): runtime.Types.Utils.JsPromise<void>;
/**
* Disconnect from the database
*/
$disconnect(): runtime.Types.Utils.JsPromise<void>;
/**
* Executes a prepared raw query and returns the number of affected rows.
* @example
* ```
* const result = await prisma.$executeRaw`UPDATE User SET cool = ${true} WHERE email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Executes a raw query and returns the number of affected rows.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$executeRawUnsafe('UPDATE User SET cool = $1 WHERE email = $2 ;', true, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$executeRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<number>;
/**
* Performs a prepared raw query and returns the `SELECT` data.
* @example
* ```
* const result = await prisma.$queryRaw`SELECT * FROM User WHERE id = ${1} OR email = ${'user@email.com'};`
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRaw<T = unknown>(query: TemplateStringsArray | Prisma.Sql, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Performs a raw query and returns the `SELECT` data.
* Susceptible to SQL injections, see documentation.
* @example
* ```
* const result = await prisma.$queryRawUnsafe('SELECT * FROM User WHERE id = $1 OR email = $2;', 1, 'user@email.com')
* ```
*
* Read more in our [docs](https://pris.ly/d/raw-queries).
*/
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Prisma.PrismaPromise<T>;
/**
* Allows the running of a sequence of read/write operations that are guaranteed to either succeed or fail as a whole.
* @example
* ```
* const [george, bob, alice] = await prisma.$transaction([
* prisma.user.create({ data: { name: 'George' } }),
* prisma.user.create({ data: { name: 'Bob' } }),
* prisma.user.create({ data: { name: 'Alice' } }),
* ])
* ```
*
* Read more in our [docs](https://www.prisma.io/docs/concepts/components/prisma-client/transactions).
*/
$transaction<P extends Prisma.PrismaPromise<any>[]>(arg: [...P], options?: { isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<runtime.Types.Utils.UnwrapTuple<P>>
$transaction<R>(fn: (prisma: Omit<PrismaClient, runtime.ITXClientDenyList>) => runtime.Types.Utils.JsPromise<R>, options?: { maxWait?: number, timeout?: number, isolationLevel?: Prisma.TransactionIsolationLevel }): runtime.Types.Utils.JsPromise<R>
$extends: runtime.Types.Extensions.ExtendsHook<"extends", Prisma.TypeMapCb<OmitOpts>, ExtArgs, runtime.Types.Utils.Call<Prisma.TypeMapCb<OmitOpts>, {
extArgs: ExtArgs
}>>
/**
* `prisma.account`: Exposes CRUD operations for the **Account** model.
* Example usage:
* ```ts
* // Fetch zero or more Accounts
* const accounts = await prisma.account.findMany()
* ```
*/
get account(): Prisma.AccountDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.appInstall`: Exposes CRUD operations for the **AppInstall** model.
* Example usage:
* ```ts
* // Fetch zero or more AppInstalls
* const appInstalls = await prisma.appInstall.findMany()
* ```
*/
get appInstall(): Prisma.AppInstallDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.autoAuth`: Exposes CRUD operations for the **AutoAuth** model.
* Example usage:
* ```ts
* // Fetch zero or more AutoAuths
* const autoAuths = await prisma.autoAuth.findMany()
* ```
*/
get autoAuth(): Prisma.AutoAuthDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.device`: Exposes CRUD operations for the **Device** model.
* Example usage:
* ```ts
* // Fetch zero or more Devices
* const devices = await prisma.device.findMany()
* ```
*/
get device(): Prisma.DeviceDelegate<ExtArgs, { omit: OmitOpts }>;
/**
* `prisma.kVStore`: Exposes CRUD operations for the **KVStore** model.
* Example usage:
* ```ts
* // Fetch zero or more KVStores
* const kVStores = await prisma.kVStore.findMany()
* ```
*/
get kVStore(): Prisma.KVStoreDelegate<ExtArgs, { omit: OmitOpts }>;
}
export function getPrismaClientClass(): PrismaClientConstructor {
return runtime.getPrismaClient(config) as unknown as PrismaClientConstructor
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,197 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* WARNING: This is an internal file that is subject to change!
*
* 🛑 Under no circumstances should you import this file directly! 🛑
*
* All exports from this file are wrapped under a `Prisma` namespace object in the browser.ts file.
* While this enables partial backward compatibility, it is not part of the stable public API.
*
* If you are looking for your Models, Enums, and Input Types, please import them from the respective
* model files in the `model` directory!
*/
import * as runtime from "@prisma/client/runtime/index-browser"
export type * from '../models.ts'
export type * from './prismaNamespace.ts'
export const Decimal = runtime.Decimal
export const NullTypes = {
DbNull: runtime.NullTypes.DbNull as (new (secret: never) => typeof runtime.DbNull),
JsonNull: runtime.NullTypes.JsonNull as (new (secret: never) => typeof runtime.JsonNull),
AnyNull: runtime.NullTypes.AnyNull as (new (secret: never) => typeof runtime.AnyNull),
}
/**
* Helper for filtering JSON entries that have `null` on the database (empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const DbNull = runtime.DbNull
/**
* Helper for filtering JSON entries that have JSON `null` values (not empty on the db)
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const JsonNull = runtime.JsonNull
/**
* Helper for filtering JSON entries that are `Prisma.DbNull` or `Prisma.JsonNull`
*
* @see https://www.prisma.io/docs/concepts/components/prisma-client/working-with-fields/working-with-json-fields#filtering-on-a-json-field
*/
export const AnyNull = runtime.AnyNull
export const ModelName = {
Account: 'Account',
AppInstall: 'AppInstall',
AutoAuth: 'AutoAuth',
Device: 'Device',
KVStore: 'KVStore'
} as const
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
/*
* Enums
*/
export const TransactionIsolationLevel = runtime.makeStrictEnum({
ReadUncommitted: 'ReadUncommitted',
ReadCommitted: 'ReadCommitted',
RepeatableRead: 'RepeatableRead',
Serializable: 'Serializable'
} as const)
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
export const AccountScalarFieldEnum = {
id: 'id',
provider: 'provider',
providerId: 'providerId',
email: 'email',
name: 'name',
avatarUrl: 'avatarUrl',
providerData: 'providerData',
accessToken: 'accessToken',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
refreshToken: 'refreshToken',
refreshTokenExpiry: 'refreshTokenExpiry',
tokenVersion: 'tokenVersion'
} as const
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
export const AppInstallScalarFieldEnum = {
id: 'id',
deviceId: 'deviceId',
appId: 'appId',
token: 'token',
note: 'note',
installedAt: 'installedAt',
updatedAt: 'updatedAt',
deviceType: 'deviceType',
isReadOnly: 'isReadOnly'
} as const
export type AppInstallScalarFieldEnum = (typeof AppInstallScalarFieldEnum)[keyof typeof AppInstallScalarFieldEnum]
export const AutoAuthScalarFieldEnum = {
id: 'id',
deviceId: 'deviceId',
password: 'password',
deviceType: 'deviceType',
isReadOnly: 'isReadOnly',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type AutoAuthScalarFieldEnum = (typeof AutoAuthScalarFieldEnum)[keyof typeof AutoAuthScalarFieldEnum]
export const DeviceScalarFieldEnum = {
id: 'id',
uuid: 'uuid',
name: 'name',
accountId: 'accountId',
createdAt: 'createdAt',
updatedAt: 'updatedAt',
password: 'password',
passwordHint: 'passwordHint',
namespace: 'namespace'
} as const
export type DeviceScalarFieldEnum = (typeof DeviceScalarFieldEnum)[keyof typeof DeviceScalarFieldEnum]
export const KVStoreScalarFieldEnum = {
deviceId: 'deviceId',
key: 'key',
value: 'value',
creatorIp: 'creatorIp',
createdAt: 'createdAt',
updatedAt: 'updatedAt'
} as const
export type KVStoreScalarFieldEnum = (typeof KVStoreScalarFieldEnum)[keyof typeof KVStoreScalarFieldEnum]
export const SortOrder = {
asc: 'asc',
desc: 'desc'
} as const
export type SortOrder = (typeof SortOrder)[keyof typeof SortOrder]
export const NullableJsonNullValueInput = {
DbNull: DbNull,
JsonNull: JsonNull
} as const
export type NullableJsonNullValueInput = (typeof NullableJsonNullValueInput)[keyof typeof NullableJsonNullValueInput]
export const JsonNullValueInput = {
JsonNull: JsonNull
} as const
export type JsonNullValueInput = (typeof JsonNullValueInput)[keyof typeof JsonNullValueInput]
export const QueryMode = {
default: 'default',
insensitive: 'insensitive'
} as const
export type QueryMode = (typeof QueryMode)[keyof typeof QueryMode]
export const JsonNullValueFilter = {
DbNull: DbNull,
JsonNull: JsonNull,
AnyNull: AnyNull
} as const
export type JsonNullValueFilter = (typeof JsonNullValueFilter)[keyof typeof JsonNullValueFilter]
export const NullsOrder = {
first: 'first',
last: 'last'
} as const
export type NullsOrder = (typeof NullsOrder)[keyof typeof NullsOrder]

View File

@ -0,0 +1,16 @@
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
/* eslint-disable */
// biome-ignore-all lint: generated file
// @ts-nocheck
/*
* This is a barrel export file for all models and their related types.
*
* 🟢 You can import this file directly.
*/
export type * from './models/Account.ts'
export type * from './models/AppInstall.ts'
export type * from './models/AutoAuth.ts'
export type * from './models/Device.ts'
export type * from './models/KVStore.ts'
export type * from './commonInputTypes.ts'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -6,13 +6,10 @@
* 2. deviceInfoMiddleware - 仅获取设备信息不创建新设备 * 2. deviceInfoMiddleware - 仅获取设备信息不创建新设备
* 3. passwordMiddleware - 验证设备密码 * 3. passwordMiddleware - 验证设备密码
*/ */
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import {verifyDevicePassword} from "../utils/crypto.js"; import {verifyDevicePassword} from "../utils/crypto.js";
import {analyzeDevice} from "../utils/deviceDetector.js"; import {analyzeDevice} from "../utils/deviceDetector.js";
import { prisma } from "../utils/prisma.js";
const prisma = new PrismaClient();
/** /**
* 为新设备创建默认的自动登录配置 * 为新设备创建默认的自动登录配置

View File

@ -8,10 +8,8 @@
import {generateAccessToken, validateAccountToken, verifyAccessToken} from "../utils/tokenManager.js"; import {generateAccessToken, validateAccountToken, verifyAccessToken} from "../utils/tokenManager.js";
import {verifyToken} from "../utils/jwt.js"; import {verifyToken} from "../utils/jwt.js";
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import { prisma } from "../utils/prisma.js";
const prisma = new PrismaClient();
/** /**
* 新的JWT认证中间件支持refresh token系统 * 新的JWT认证中间件支持refresh token系统

View File

@ -4,11 +4,8 @@
* 仅验证app token设置设备和应用信息到res.locals * 仅验证app token设置设备和应用信息到res.locals
* 适用于所有KV相关的接口 * 适用于所有KV相关的接口
*/ */
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import { prisma } from "../utils/prisma.js";
const prisma = new PrismaClient();
/** /**
* KV Token认证中间件 * KV Token认证中间件

View File

@ -5,13 +5,10 @@
* 2. 验证密码或账户JWT二选一 * 2. 验证密码或账户JWT二选一
* 3. 适用于需要设备上下文的接口 * 3. 适用于需要设备上下文的接口
*/ */
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import {verifyToken as verifyAccountJWT} from "../utils/jwt.js"; import {verifyToken as verifyAccountJWT} from "../utils/jwt.js";
import {verifyDevicePassword} from "../utils/crypto.js"; import {verifyDevicePassword} from "../utils/crypto.js";
import { prisma } from "../utils/prisma.js";
const prisma = new PrismaClient();
/** /**
* UUID+密码/JWT混合认证中间件 * UUID+密码/JWT混合认证中间件

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "ClassworksKV", "name": "ClassworksKV",
"version": "1.3.9", "version": "1.3.15",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "ClassworksKV", "name": "ClassworksKV",
"version": "1.3.9", "version": "1.3.15",
"dependencies": { "dependencies": {
"@opentelemetry/auto-instrumentations-node": "^0.59.0", "@opentelemetry/auto-instrumentations-node": "^0.59.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.205.0", "@opentelemetry/exporter-trace-otlp-proto": "^0.205.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "ClassworksKV", "name": "ClassworksKV",
"version": "1.3.9", "version": "1.3.15",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./bin/www", "start": "node ./bin/www",
@ -15,7 +15,8 @@
"@opentelemetry/sdk-node": "^0.201.1", "@opentelemetry/sdk-node": "^0.201.1",
"@opentelemetry/sdk-trace-base": "^2.0.1", "@opentelemetry/sdk-trace-base": "^2.0.1",
"@opentelemetry/semantic-conventions": "^1.34.0", "@opentelemetry/semantic-conventions": "^1.34.0",
"@prisma/client": "6.16.2", "@prisma/adapter-pg": "^7.3.0",
"@prisma/client": "^7.3.0",
"axios": "^1.9.0", "axios": "^1.9.0",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"body-parser": "^2.2.0", "body-parser": "^2.2.0",
@ -32,10 +33,12 @@
"morgan": "~1.10.0", "morgan": "~1.10.0",
"node-device-detector": "^2.2.4", "node-device-detector": "^2.2.4",
"prom-client": "^15.1.3", "prom-client": "^15.1.3",
"pg": "^8.18.0",
"socket.io": "^4.8.1", "socket.io": "^4.8.1",
"uuid": "^11.1.0" "uuid": "^11.1.0"
}, },
"devDependencies": { "devDependencies": {
"prisma": "^6.18.0" "dotenv": "^16.5.0",
"prisma": "^7.3.0"
} }
} }

584
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

14
prisma.config.js Normal file
View File

@ -0,0 +1,14 @@
// This file was generated by Prisma, and assumes you have installed the following:
// npm install --save-dev prisma dotenv
import "dotenv/config";
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env["DATABASE_URL"],
},
});

View File

@ -0,0 +1,110 @@
-- CreateSchema
CREATE SCHEMA IF NOT EXISTS "public";
-- CreateTable
CREATE TABLE "Account" (
"id" VARCHAR(191) NOT NULL,
"provider" VARCHAR(191) NOT NULL,
"providerId" VARCHAR(191) NOT NULL,
"email" VARCHAR(191),
"name" VARCHAR(191),
"avatarUrl" VARCHAR(191),
"providerData" JSON,
"accessToken" TEXT,
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"refreshToken" TEXT,
"refreshTokenExpiry" TIMESTAMPTZ(6),
"tokenVersion" INTEGER NOT NULL DEFAULT 1,
CONSTRAINT "idx_18303_PRIMARY" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AppInstall" (
"id" VARCHAR(191) NOT NULL,
"deviceId" INTEGER NOT NULL,
"appId" VARCHAR(191) NOT NULL,
"token" VARCHAR(191) NOT NULL,
"note" VARCHAR(191),
"installedAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"deviceType" VARCHAR(191),
"isReadOnly" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "idx_18310_PRIMARY" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AutoAuth" (
"id" VARCHAR(191) NOT NULL,
"deviceId" INTEGER NOT NULL,
"password" VARCHAR(191),
"deviceType" VARCHAR(191),
"isReadOnly" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "idx_18317_PRIMARY" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Device" (
"id" INTEGER NOT NULL,
"uuid" VARCHAR(191) NOT NULL,
"name" VARCHAR(191),
"accountId" VARCHAR(191),
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
"password" VARCHAR(191),
"passwordHint" VARCHAR(191),
"namespace" VARCHAR(191),
CONSTRAINT "idx_18324_PRIMARY" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "KVStore" (
"deviceId" INTEGER NOT NULL,
"key" VARCHAR(191) NOT NULL,
"value" JSON NOT NULL,
"creatorIp" VARCHAR(191) DEFAULT '',
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "idx_18330_PRIMARY" PRIMARY KEY ("deviceId","key")
);
-- CreateIndex
CREATE UNIQUE INDEX "idx_18303_Account_provider_providerId_key" ON "Account"("provider", "providerId");
-- CreateIndex
CREATE UNIQUE INDEX "idx_18310_AppInstall_token_key" ON "AppInstall"("token");
-- CreateIndex
CREATE INDEX "idx_18310_AppInstall_deviceId_fkey" ON "AppInstall"("deviceId");
-- CreateIndex
CREATE UNIQUE INDEX "idx_18317_AutoAuth_deviceId_password_key" ON "AutoAuth"("deviceId", "password");
-- CreateIndex
CREATE UNIQUE INDEX "idx_18324_Device_uuid_key" ON "Device"("uuid");
-- CreateIndex
CREATE UNIQUE INDEX "idx_18324_Device_namespace_key" ON "Device"("namespace");
-- CreateIndex
CREATE INDEX "idx_18324_Device_accountId_fkey" ON "Device"("accountId");
-- AddForeignKey
ALTER TABLE "AppInstall" ADD CONSTRAINT "AppInstall_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "Device"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AutoAuth" ADD CONSTRAINT "AutoAuth_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "Device"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Device" ADD CONSTRAINT "Device_accountId_fkey" FOREIGN KEY ("accountId") REFERENCES "Account"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "KVStore" ADD CONSTRAINT "KVStore_deviceId_fkey" FOREIGN KEY ("deviceId") REFERENCES "Device"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,68 +0,0 @@
-- CreateTable
CREATE TABLE `KVStore` (
`deviceId` INTEGER NOT NULL,
`key` VARCHAR(191) NOT NULL,
`value` JSON NOT NULL,
`creatorIp` VARCHAR(191) NULL DEFAULT '',
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
PRIMARY KEY (`deviceId`, `key`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Account` (
`id` VARCHAR(191) NOT NULL,
`provider` VARCHAR(191) NOT NULL,
`providerId` VARCHAR(191) NOT NULL,
`email` VARCHAR(191) NULL,
`name` VARCHAR(191) NULL,
`avatarUrl` VARCHAR(191) NULL,
`providerData` JSON NULL,
`accessToken` VARCHAR(191) NOT NULL,
`refreshToken` VARCHAR(191) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `Account_accessToken_key`(`accessToken`),
UNIQUE INDEX `Account_provider_providerId_key`(`provider`, `providerId`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `Device` (
`id` INTEGER NOT NULL AUTO_INCREMENT,
`uuid` VARCHAR(191) NOT NULL,
`name` VARCHAR(191) NULL,
`accountId` VARCHAR(191) NULL,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
`password` VARCHAR(191) NULL,
`passwordHint` VARCHAR(191) NULL,
UNIQUE INDEX `Device_uuid_key`(`uuid`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- CreateTable
CREATE TABLE `AppInstall` (
`id` VARCHAR(191) NOT NULL,
`deviceId` INTEGER NOT NULL,
`appId` VARCHAR(191) NOT NULL,
`token` VARCHAR(191) NOT NULL,
`note` VARCHAR(191) NULL,
`installedAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `AppInstall_token_key`(`token`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- AddForeignKey
ALTER TABLE `KVStore` ADD CONSTRAINT `KVStore_deviceId_fkey` FOREIGN KEY (`deviceId`) REFERENCES `Device`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `Device` ADD CONSTRAINT `Device_accountId_fkey` FOREIGN KEY (`accountId`) REFERENCES `Account`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE `AppInstall` ADD CONSTRAINT `AppInstall_deviceId_fkey` FOREIGN KEY (`deviceId`) REFERENCES `Device`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,2 +0,0 @@
-- AlterTable
ALTER TABLE `Account` MODIFY `refreshToken` TEXT NULL;

View File

@ -1,5 +0,0 @@
-- DropIndex
DROP INDEX `Account_accessToken_key` ON `Account`;
-- AlterTable
ALTER TABLE `Account` MODIFY `accessToken` TEXT NOT NULL;

View File

@ -1 +0,0 @@
-- This is an empty migration.

View File

@ -1,9 +0,0 @@
/*
Warnings:
- You are about to drop the column `refreshToken` on the `Account` table. All the data in the column will be lost.
*/
-- AlterTable
ALTER TABLE `Account` DROP COLUMN `refreshToken`,
MODIFY `accessToken` TEXT NULL;

View File

@ -1,47 +0,0 @@
/*
Warnings:
- A unique constraint covering the columns `[namespace]` on the table `Device` will be added. If there are existing duplicate values, this will fail.
*/
-- AlterTable
ALTER TABLE `AppInstall` ADD COLUMN `deviceType` VARCHAR(191) NULL,
ADD COLUMN `isReadOnly` BOOLEAN NOT NULL DEFAULT false;
-- AlterTable
ALTER TABLE `Device` ADD COLUMN `namespace` VARCHAR(191) NULL;
-- 将所有设备的 namespace 设置为对应的 uuid 值,避免唯一键冲突
UPDATE `Device` SET `namespace` = `uuid` WHERE `namespace` IS NULL;
-- CreateTable
CREATE TABLE `AutoAuth` (
`id` VARCHAR(191) NOT NULL,
`deviceId` INTEGER NOT NULL,
`password` VARCHAR(191) NULL,
`deviceType` VARCHAR(191) NULL,
`isReadOnly` BOOLEAN NOT NULL DEFAULT false,
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updatedAt` DATETIME(3) NOT NULL,
UNIQUE INDEX `AutoAuth_deviceId_password_key`(`deviceId`, `password`),
PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 为每个设备创建默认的 AutoAuth 记录,将 Device.password 复制为 AutoAuth.password
INSERT INTO `AutoAuth` (`id`, `deviceId`, `password`, `deviceType`, `isReadOnly`, `createdAt`, `updatedAt`)
SELECT
CONCAT('autoauth_', UUID()),
`id`,
`password`,
NULL,
false,
CURRENT_TIMESTAMP(3),
CURRENT_TIMESTAMP(3)
FROM `Device`;
-- CreateIndex
CREATE UNIQUE INDEX `Device_namespace_key` ON `Device`(`namespace`);
-- AddForeignKey
ALTER TABLE `AutoAuth` ADD CONSTRAINT `AutoAuth_deviceId_fkey` FOREIGN KEY (`deviceId`) REFERENCES `Device`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -1,4 +0,0 @@
-- AlterTable
ALTER TABLE `Account` ADD COLUMN `refreshToken` TEXT NULL,
ADD COLUMN `refreshTokenExpiry` DATETIME(3) NULL,
ADD COLUMN `tokenVersion` INTEGER NOT NULL DEFAULT 1;

View File

@ -0,0 +1,14 @@
-- AlterTable
ALTER TABLE "Account" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP;
-- AlterTable
ALTER TABLE "AppInstall" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP;
-- AlterTable
ALTER TABLE "AutoAuth" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP;
-- AlterTable
ALTER TABLE "Device" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP;
-- AlterTable
ALTER TABLE "KVStore" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP;

View File

@ -0,0 +1,4 @@
-- AlterTable
CREATE SEQUENCE device_id_seq;
ALTER TABLE "Device" ALTER COLUMN "id" SET DEFAULT nextval('device_id_seq');
ALTER SEQUENCE device_id_seq OWNED BY "Device"."id";

View File

@ -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 = "postgresql"

View File

@ -1,91 +1,90 @@
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client"
output = "../generated/prisma"
} }
datasource db { datasource db {
provider = "mysql" provider = "postgresql"
url = env("DATABASE_URL")
}
model KVStore {
deviceId Int
key String
value Json
creatorIp String? @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@id([deviceId, key])
} }
model Account { model Account {
id String @id @default(cuid()) id String @id(map: "idx_18303_PRIMARY") @db.VarChar(191) @default(cuid())
provider String // OAuth提供者 (例如: google, github, gitlab等) provider String @db.VarChar(191)
providerId String // 提供者返回的用户唯一ID providerId String @db.VarChar(191)
email String? // 用户邮箱 email String? @db.VarChar(191)
name String? // 用户名称 name String? @db.VarChar(191)
avatarUrl String? // 用户头像URL avatarUrl String? @db.VarChar(191)
providerData Json? // OAuth提供者返回的完整信息 providerData Json? @db.Json
accessToken String? @db.Text // 账户访问令牌 accessToken String?
refreshToken String? @db.Text // 刷新令牌 createdAt DateTime @default(now()) @db.Timestamptz(6)
refreshTokenExpiry DateTime? // 刷新令牌过期时间 updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
tokenVersion Int @default(1) // 令牌版本,用于令牌失效 refreshToken String?
createdAt DateTime @default(now()) refreshTokenExpiry DateTime? @db.Timestamptz(6)
updatedAt DateTime @updatedAt tokenVersion Int @default(1)
// 关联的设备
devices Device[] devices Device[]
@@unique([provider, providerId]) // 确保同一提供者的用户ID唯一 @@unique([provider, providerId], map: "idx_18303_Account_provider_providerId_key")
}
model AppInstall {
id String @id(map: "idx_18310_PRIMARY") @default(cuid()) @db.VarChar(191)
deviceId Int
appId String @db.VarChar(191)
token String @unique(map: "idx_18310_AppInstall_token_key") @db.VarChar(191)
note String? @db.VarChar(191)
installedAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
deviceType String? @db.VarChar(191)
isReadOnly Boolean @default(false)
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@index([deviceId], map: "idx_18310_AppInstall_deviceId_fkey")
}
model AutoAuth {
id String @id(map: "idx_18317_PRIMARY") @default(cuid()) @db.VarChar(191)
deviceId Int
password String? @db.VarChar(191)
deviceType String? @db.VarChar(191)
isReadOnly Boolean @default(false)
createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@unique([deviceId, password], map: "idx_18317_AutoAuth_deviceId_password_key")
} }
model Device { model Device {
id Int @id @default(autoincrement()) id Int @id(map: "idx_18324_PRIMARY") @default(autoincrement())
uuid String @unique // 设备的唯一标识符 uuid String @unique(map: "idx_18324_Device_uuid_key") @db.VarChar(191)
name String? name String? @db.VarChar(191)
accountId String? // 关联的账户ID accountId String? @db.VarChar(191)
createdAt DateTime @default(now()) createdAt DateTime @default(now()) @db.Timestamptz(6)
updatedAt DateTime @updatedAt updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
password String? password String? @db.VarChar(191)
passwordHint String? passwordHint String? @db.VarChar(191)
namespace String? @unique // 用户自定义的唯一命名空间 namespace String? @unique(map: "idx_18324_Device_namespace_key") @db.VarChar(191)
// 关联关系 // 关联关系
account Account? @relation(fields: [accountId], references: [id], onDelete: SetNull) account Account? @relation(fields: [accountId], references: [id], onDelete: SetNull)
appInstalls AppInstall[] appInstalls AppInstall[]
kvStore KVStore[] // 设备相关的KV存储 kvStore KVStore[] // 设备相关的KV存储
autoAuths AutoAuth[] // 自动授权配置 autoAuths AutoAuth[] // 自动授权配置
@@index([accountId], map: "idx_18324_Device_accountId_fkey")
} }
model AppInstall { model KVStore {
id String @id @default(cuid()) deviceId Int
deviceId Int // 关联的设备ID key String @db.VarChar(191)
appId String // 应用ID (SHA256 hash) value Json @db.Json
token String @unique // 应用安装的唯一访问令牌,拥有完整KV读写权限 creatorIp String? @default("") @db.VarChar(191)
note String? // 安装备注 createdAt DateTime @default(now()) @db.Timestamptz(6)
isReadOnly Boolean @default(false) // 是否只读 updatedAt DateTime @default(now()) @updatedAt @db.Timestamptz(6)
deviceType String? // 设备类型: teacher(教师), student(学生), classroom(班级一体机), parent(家长)
installedAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系 device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
} @@id([deviceId, key], map: "idx_18330_PRIMARY")
model AutoAuth {
id String @id @default(cuid())
deviceId Int // 关联的设备ID
password String? // 配置密码,可以为空
deviceType String? // 自动设备类型: teacher(教师), student(学生), classroom(班级一体机), parent(家长)
isReadOnly Boolean @default(false) // 是否只读
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// 关联关系
device Device @relation(fields: [deviceId], references: [id], onDelete: Cascade)
@@unique([deviceId, password]) // 同一设备的密码必须唯一
} }

View File

@ -1,13 +1,12 @@
import {Router} from "express"; import {Router} from "express";
import {PrismaClient} from "@prisma/client";
import crypto from "crypto"; import crypto from "crypto";
import {generateState, getCallbackURL, oauthProviders} from "../config/oauth.js"; import {generateState, getCallbackURL, oauthProviders} from "../config/oauth.js";
import {generateTokenPair, refreshAccessToken, revokeAllTokens, revokeRefreshToken} from "../utils/jwt.js"; import {generateTokenPair, refreshAccessToken, revokeAllTokens, revokeRefreshToken} from "../utils/jwt.js";
import {jwtAuth} from "../middleware/jwt-auth.js"; import {jwtAuth} from "../middleware/jwt-auth.js";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
// 存储OAuth state防止CSRF攻击生产环境应使用Redis等 // 存储OAuth state防止CSRF攻击生产环境应使用Redis等
const oauthStates = new Map(); const oauthStates = new Map();

View File

@ -1,14 +1,12 @@
import {Router} from "express"; import {Router} from "express";
import {uuidAuth} from "../middleware/uuidAuth.js"; import {uuidAuth} from "../middleware/uuidAuth.js";
import {PrismaClient} from "@prisma/client";
import crypto from "crypto"; import crypto from "crypto";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import {verifyDevicePassword} from "../utils/crypto.js"; import {verifyDevicePassword} from "../utils/crypto.js";
import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
/** /**
* GET /apps/devices/:uuid/apps * GET /apps/devices/:uuid/apps
* 获取设备安装的应用列表 (公开接口无需认证) * 获取设备安装的应用列表 (公开接口无需认证)

View File

@ -1,12 +1,10 @@
import {Router} from "express"; import {Router} from "express";
import {jwtAuth} from "../middleware/jwt-auth.js"; import {jwtAuth} from "../middleware/jwt-auth.js";
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
/** /**
* GET /auto-auth/devices/:uuid/auth-configs * GET /auto-auth/devices/:uuid/auth-configs
* 获取设备的所有自动授权配置 (需要 JWT 认证且设备必须绑定到该账户) * 获取设备的所有自动授权配置 (需要 JWT 认证且设备必须绑定到该账户)

View File

@ -1,10 +1,9 @@
import {Router} from "express"; import {Router} from "express";
import deviceCodeStore from "../utils/deviceCodeStore.js"; import deviceCodeStore from "../utils/deviceCodeStore.js";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import {PrismaClient} from "@prisma/client"; import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
/** /**

View File

@ -1,14 +1,12 @@
import {Router} from "express"; import {Router} from "express";
import {extractDeviceInfo} from "../middleware/uuidAuth.js"; import {extractDeviceInfo} from "../middleware/uuidAuth.js";
import {PrismaClient} from "@prisma/client";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import {getOnlineDevices} from "../utils/socket.js"; import {getOnlineDevices} from "../utils/socket.js";
import {registeredDevicesTotal} from "../utils/metrics.js"; import {registeredDevicesTotal} from "../utils/metrics.js";
import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
/** /**
* 为新设备创建默认的自动登录配置 * 为新设备创建默认的自动登录配置
* @param {number} deviceId - 设备ID * @param {number} deviceId - 设备ID

View File

@ -10,12 +10,10 @@ import {
tokenWriteLimiter tokenWriteLimiter
} from "../middleware/rateLimiter.js"; } from "../middleware/rateLimiter.js";
import errors from "../utils/errors.js"; import errors from "../utils/errors.js";
import { PrismaClient } from "@prisma/client"; import { prisma } from "../utils/prisma.js";
const router = Router(); const router = Router();
const prisma = new PrismaClient();
// 使用KV专用token认证 // 使用KV专用token认证
router.use(kvTokenAuth); router.use(kvTokenAuth);

View File

@ -1,7 +1,5 @@
import {PrismaClient} from "@prisma/client";
import {keysTotal} from "./metrics.js"; import {keysTotal} from "./metrics.js";
import { prisma } from "./prisma.js";
const prisma = new PrismaClient();
class KVStore { class KVStore {
/** /**

View File

@ -1,4 +1,5 @@
import client from 'prom-client'; import client from 'prom-client';
import { prisma } from './prisma.js';
// 创建自定义注册表(不包含默认指标) // 创建自定义注册表(不包含默认指标)
const register = new client.Registry(); const register = new client.Registry();
@ -27,9 +28,6 @@ export const keysTotal = new client.Gauge({
// 初始化指标数据 // 初始化指标数据
export async function initializeMetrics() { export async function initializeMetrics() {
try { try {
const {PrismaClient} = await import('@prisma/client');
const prisma = new PrismaClient();
// 获取已注册设备总数 // 获取已注册设备总数
const deviceCount = await prisma.device.count(); const deviceCount = await prisma.device.count();
registeredDevicesTotal.set(deviceCount); registeredDevicesTotal.set(deviceCount);
@ -37,8 +35,6 @@ export async function initializeMetrics() {
// 获取已创建键总数 // 获取已创建键总数
const keyCount = await prisma.kVStore.count(); const keyCount = await prisma.kVStore.count();
keysTotal.set(keyCount); keysTotal.set(keyCount);
await prisma.$disconnect();
console.log('Prometheus metrics initialized - Devices:', deviceCount, 'Keys:', keyCount); console.log('Prometheus metrics initialized - Devices:', deviceCount, 'Keys:', keyCount);
} catch (error) { } catch (error) {
console.error('Failed to initialize metrics:', error); console.error('Failed to initialize metrics:', error);

10
utils/prisma.js Normal file
View File

@ -0,0 +1,10 @@
import "dotenv/config";
import { PrismaPg } from '@prisma/adapter-pg'
import { PrismaClient } from '../generated/prisma/client.ts'
const connectionString = `${process.env.DATABASE_URL}`
const adapter = new PrismaPg({ connectionString })
const prisma = new PrismaClient({ adapter })
export { prisma }

View File

@ -1,7 +1,5 @@
import {PrismaClient} from "@prisma/client";
import kvStore from "./kvStore.js"; import kvStore from "./kvStore.js";
import { prisma } from "./prisma.js";
const prisma = new PrismaClient();
// 系统保留UUID用于存储站点信息 // 系统保留UUID用于存储站点信息
const SYSTEM_DEVICE_UUID = "00000000-0000-4000-8000-000000000000"; const SYSTEM_DEVICE_UUID = "00000000-0000-4000-8000-000000000000";

View File

@ -13,10 +13,10 @@
*/ */
import { Server } from "socket.io"; import { Server } from "socket.io";
import { PrismaClient } from "@prisma/client";
import { onlineDevicesGauge } from "./metrics.js"; import { onlineDevicesGauge } from "./metrics.js";
import DeviceDetector from "node-device-detector"; import DeviceDetector from "node-device-detector";
import ClientHints from "node-device-detector/client-hints.js"; import ClientHints from "node-device-detector/client-hints.js";
import { prisma } from "./prisma.js";
// Socket.IO 单例实例 // Socket.IO 单例实例
let io = null; let io = null;
@ -38,7 +38,6 @@ const tokenInfoCache = new Map();
// 事件历史记录每个设备最多保存1000条事件记录 // 事件历史记录每个设备最多保存1000条事件记录
const eventHistory = new Map(); // uuid -> Array<EventRecord> const eventHistory = new Map(); // uuid -> Array<EventRecord>
const MAX_EVENT_HISTORY = 1000; const MAX_EVENT_HISTORY = 1000;
const prisma = new PrismaClient();
/** /**
* 检测设备并生成友好的设备名称 * 检测设备并生成友好的设备名称

View File

@ -1,8 +1,6 @@
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import crypto from 'crypto'; import crypto from 'crypto';
import {PrismaClient} from '@prisma/client'; import { prisma } from './prisma.js';
const prisma = new PrismaClient();
// Token 配置 // Token 配置
const ACCESS_TOKEN_SECRET = process.env.JWT_SECRET || 'your-access-token-secret-change-this-in-production'; const ACCESS_TOKEN_SECRET = process.env.JWT_SECRET || 'your-access-token-secret-change-this-in-production';