diff --git a/component/_generated/api.ts b/component/_generated/api.ts new file mode 100644 index 0000000..22a125c --- /dev/null +++ b/component/_generated/api.ts @@ -0,0 +1,46 @@ +/* eslint-disable */ +/** + * Generated `api` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { + ApiFromModules, + FilterApi, + FunctionReference, +} from "convex/server"; +import { anyApi, componentsGeneric } from "convex/server"; + +const fullApi: ApiFromModules<{}> = anyApi as any; + +/** + * A utility for referencing Convex functions in your app's public API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +export const api: FilterApi< + typeof fullApi, + FunctionReference +> = anyApi as any; + +/** + * A utility for referencing Convex functions in your app's internal API. + * + * Usage: + * ```js + * const myFunctionReference = internal.myModule.myFunction; + * ``` + */ +export const internal: FilterApi< + typeof fullApi, + FunctionReference +> = anyApi as any; + +export const components = componentsGeneric() as unknown as {}; diff --git a/component/_generated/component.ts b/component/_generated/component.ts new file mode 100644 index 0000000..638c09a --- /dev/null +++ b/component/_generated/component.ts @@ -0,0 +1,25 @@ +/* eslint-disable */ +/** + * Generated `ComponentApi` utility. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { FunctionReference } from "convex/server"; + +/** + * A utility for referencing a Convex component's exposed API. + * + * Useful when expecting a parameter like `components.myComponent`. + * Usage: + * ```ts + * async function myFunction(ctx: QueryCtx, component: ComponentApi) { + * return ctx.runQuery(component.someFile.someQuery, { ...args }); + * } + * ``` + */ +export type ComponentApi = + {}; diff --git a/component/_generated/dataModel.ts b/component/_generated/dataModel.ts new file mode 100644 index 0000000..f97fd19 --- /dev/null +++ b/component/_generated/dataModel.ts @@ -0,0 +1,60 @@ +/* eslint-disable */ +/** + * Generated data model types. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { + DataModelFromSchemaDefinition, + DocumentByName, + TableNamesInDataModel, + SystemTableNames, +} from "convex/server"; +import type { GenericId } from "convex/values"; +import schema from "../schema.js"; + +/** + * The names of all of your Convex tables. + */ +export type TableNames = TableNamesInDataModel; + +/** + * The type of a document stored in Convex. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Doc = DocumentByName< + DataModel, + TableName +>; + +/** + * An identifier for a document in Convex. + * + * Convex documents are uniquely identified by their `Id`, which is accessible + * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids). + * + * Documents can be loaded using `db.get(tableName, id)` in query and mutation functions. + * + * IDs are just strings at runtime, but this type can be used to distinguish them from other + * strings when type checking. + * + * @typeParam TableName - A string literal type of the table name (like "users"). + */ +export type Id = + GenericId; + +/** + * A type describing your Convex data model. + * + * This type includes information about what tables you have, the type of + * documents stored in those tables, and the indexes defined on them. + * + * This type is used to parameterize methods like `queryGeneric` and + * `mutationGeneric` to make them type-safe. + */ +export type DataModel = DataModelFromSchemaDefinition; diff --git a/component/_generated/server.ts b/component/_generated/server.ts new file mode 100644 index 0000000..739b02f --- /dev/null +++ b/component/_generated/server.ts @@ -0,0 +1,156 @@ +/* eslint-disable */ +/** + * Generated utilities for implementing server-side Convex query and mutation functions. + * + * THIS CODE IS AUTOMATICALLY GENERATED. + * + * To regenerate, run `npx convex dev`. + * @module + */ + +import type { + ActionBuilder, + HttpActionBuilder, + MutationBuilder, + QueryBuilder, + GenericActionCtx, + GenericMutationCtx, + GenericQueryCtx, + GenericDatabaseReader, + GenericDatabaseWriter, +} from "convex/server"; +import { + actionGeneric, + httpActionGeneric, + queryGeneric, + mutationGeneric, + internalActionGeneric, + internalMutationGeneric, + internalQueryGeneric, +} from "convex/server"; +import type { DataModel } from "./dataModel.js"; + +/** + * Define a query in this Convex app's public API. + * + * This function will be allowed to read your Convex database and will be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export const query: QueryBuilder = queryGeneric; + +/** + * Define a query that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to read from your Convex database. It will not be accessible from the client. + * + * @param func - The query function. It receives a {@link QueryCtx} as its first argument. + * @returns The wrapped query. Include this as an `export` to name it and make it accessible. + */ +export const internalQuery: QueryBuilder = + internalQueryGeneric; + +/** + * Define a mutation in this Convex app's public API. + * + * This function will be allowed to modify your Convex database and will be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export const mutation: MutationBuilder = mutationGeneric; + +/** + * Define a mutation that is only accessible from other Convex functions (but not from the client). + * + * This function will be allowed to modify your Convex database. It will not be accessible from the client. + * + * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument. + * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible. + */ +export const internalMutation: MutationBuilder = + internalMutationGeneric; + +/** + * Define an action in this Convex app's public API. + * + * An action is a function which can execute any JavaScript code, including non-deterministic + * code and code with side-effects, like calling third-party services. + * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive. + * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}. + * + * @param func - The action. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped action. Include this as an `export` to name it and make it accessible. + */ +export const action: ActionBuilder = actionGeneric; + +/** + * Define an action that is only accessible from other Convex functions (but not from the client). + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument. + * @returns The wrapped function. Include this as an `export` to name it and make it accessible. + */ +export const internalAction: ActionBuilder = + internalActionGeneric; + +/** + * Define an HTTP action. + * + * The wrapped function will be used to respond to HTTP requests received + * by a Convex deployment if the requests matches the path and method where + * this action is routed. Be sure to route your httpAction in `convex/http.js`. + * + * @param func - The function. It receives an {@link ActionCtx} as its first argument + * and a Fetch API `Request` object as its second. + * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. + */ +export const httpAction: HttpActionBuilder = httpActionGeneric; + +/** + * A set of services for use within Convex query functions. + * + * The query context is passed as the first argument to any Convex query + * function run on the server. + * + * If you're using code generation, use the `QueryCtx` type in `convex/_generated/server.d.ts` instead. + */ +export type QueryCtx = GenericQueryCtx; + +/** + * A set of services for use within Convex mutation functions. + * + * The mutation context is passed as the first argument to any Convex mutation + * function run on the server. + * + * If you're using code generation, use the `MutationCtx` type in `convex/_generated/server.d.ts` instead. + */ +export type MutationCtx = GenericMutationCtx; + +/** + * A set of services for use within Convex action functions. + * + * The action context is passed as the first argument to any Convex action + * function run on the server. + */ +export type ActionCtx = GenericActionCtx; + +/** + * An interface to read from the database within Convex query functions. + * + * The two entry points are {@link DatabaseReader.get}, which fetches a single + * document by its {@link Id}, or {@link DatabaseReader.query}, which starts + * building a query. + */ +export type DatabaseReader = GenericDatabaseReader; + +/** + * An interface to read from and write to the database within Convex mutation + * functions. + * + * Convex guarantees that all writes within a single mutation are + * executed atomically, so you never have to worry about partial writes leaving + * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control) + * for the guarantees Convex provides your functions. + */ +export type DatabaseWriter = GenericDatabaseWriter; diff --git a/component/convex.config.ts b/component/convex.config.ts new file mode 100644 index 0000000..47506c2 --- /dev/null +++ b/component/convex.config.ts @@ -0,0 +1,5 @@ +'use node'; +import { defineComponent } from "convex/server"; +const component = defineComponent("component"); + +export default component; \ No newline at end of file diff --git a/component/schema.ts b/component/schema.ts new file mode 100644 index 0000000..3338b21 --- /dev/null +++ b/component/schema.ts @@ -0,0 +1,9 @@ +import { defineSchema, defineTable } from "convex/server"; +import { v } from "convex/values"; + +// Define a messages table with an index. +export default defineSchema({ + // test2: defineTable({ + // title: v.string(), + // }), +}); \ No newline at end of file diff --git a/component/test2.ts b/component/test2.ts new file mode 100644 index 0000000..17d3f40 --- /dev/null +++ b/component/test2.ts @@ -0,0 +1,26 @@ +// import { query, mutation, action } from "./_generated/server"; +// import { v } from "convex/values"; +// export const get = query({ +// args: {}, +// handler: async (ctx) => { +// const auth = await ctx.auth.getUserIdentity(); +// console.log("Query test2 called, auth:", auth); +// if (auth) { +// console.log("Authenticated user ID:", auth.subject); +// } +// return await ctx.db.query("test2").collect(); +// }, +// }); + +// export const get2 = query({ +// args: {}, +// handler: async (ctx) => { +// const auth = await ctx.auth.getUserIdentity(); +// console.log("Query test2 called, auth:", auth); +// if (auth) { +// console.log("Authenticated user ID:", auth.subject); +// } +// return await ctx.db.query("test2").collect(); +// }, +// }); + diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index 84c5f6a..5ffdf4d 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -8,11 +8,8 @@ * @module */ -import type * as abcv from "../abcv.js"; import type * as actions_jwt from "../actions/jwt.js"; -import type * as actions_redis from "../actions/redis.js"; import type * as http from "../http.js"; -import type * as xiong from "../xiong.js"; import type { ApiFromModules, @@ -21,11 +18,8 @@ import type { } from "convex/server"; declare const fullApi: ApiFromModules<{ - abcv: typeof abcv; "actions/jwt": typeof actions_jwt; - "actions/redis": typeof actions_redis; http: typeof http; - xiong: typeof xiong; }>; /** @@ -54,4 +48,6 @@ export declare const internal: FilterApi< FunctionReference >; -export declare const components: {}; +export declare const components: { + component: {}; +}; diff --git a/convex/abcv.ts b/convex/abcv.ts index c8a3a72..81e8fda 100644 --- a/convex/abcv.ts +++ b/convex/abcv.ts @@ -1,30 +1,30 @@ -import { query, mutation, action } from "./_generated/server"; -import { Kevisual } from '@kevisual/ai/browser' -import { v } from "convex/values"; -export const get = query({ - args: {}, - handler: async (ctx) => { - const auth = await ctx.auth.getUserIdentity(); - console.log("Query abcv.get called, auth:", auth); - if (auth) { - console.log("Authenticated user ID:", auth.subject); - } - return await ctx.db.query("abcv").collect(); - }, -}); +// import { query, mutation, action } from "./_generated/server"; +// import { Kevisual } from '@kevisual/ai/browser' +// import { v } from "convex/values"; +// export const get = query({ +// args: {}, +// handler: async (ctx) => { +// const auth = await ctx.auth.getUserIdentity(); +// console.log("Query abcv.get called, auth:", auth); +// if (auth) { +// console.log("Authenticated user ID:", auth.subject); +// } +// return await ctx.db.query("abcv").collect(); +// }, +// }); -export const chat = action({ - args: { message: v.string() }, - handler: async (ctx, { message }) => { - const kevisual = new Kevisual({ - apiKey: process.env.KEVISUAL_NEW_API_KEY || "", - }); +// export const chat = action({ +// args: { message: v.string() }, +// handler: async (ctx, { message }) => { +// const kevisual = new Kevisual({ +// apiKey: process.env.KEVISUAL_NEW_API_KEY || "", +// }); - const response = await kevisual.chat({ - messages: [ - { role: "user", content: message } - ] - }) - return kevisual.responseText; - }, -}); \ No newline at end of file +// const response = await kevisual.chat({ +// messages: [ +// { role: "user", content: message } +// ] +// }) +// return kevisual.responseText; +// }, +// }); \ No newline at end of file diff --git a/convex/actions/redis.ts b/convex/actions/redis.ts index 8407254..506ed60 100644 --- a/convex/actions/redis.ts +++ b/convex/actions/redis.ts @@ -1,22 +1,22 @@ -'use node'; +// 'use node'; -import { query, mutation, action } from "../_generated/server"; -import { Kevisual } from '@kevisual/ai/browser' -import { v } from "convex/values"; -import { Redis } from "ioredis"; -const redisClient = new Redis({ - host: process.env.REDIS_HOST, - password: process.env.REDIS_PASSWORD, -}); -let time: any = null; -export const isConnected = action({ - args: {}, - handler: async (ctx) => { - const result = await redisClient.ping(); - if (time === null) { - time = new Date(); - } - console.log("Redis PING at", new Date().toISOString(), "since", time); - return result === "PONG"; - }, -}); \ No newline at end of file +// import { query, mutation, action } from "../_generated/server"; +// import { Kevisual } from '@kevisual/ai/browser' +// import { v } from "convex/values"; +// import { Redis } from "ioredis"; +// const redisClient = new Redis({ +// host: process.env.REDIS_HOST, +// password: process.env.REDIS_PASSWORD, +// }); +// let time: any = null; +// export const isConnected = action({ +// args: {}, +// handler: async (ctx) => { +// const result = await redisClient.ping(); +// if (time === null) { +// time = new Date(); +// } +// console.log("Redis PING at", new Date().toISOString(), "since", time); +// return result === "PONG"; +// }, +// }); \ No newline at end of file diff --git a/convex/convex.config.ts b/convex/convex.config.ts new file mode 100644 index 0000000..6a4cbab --- /dev/null +++ b/convex/convex.config.ts @@ -0,0 +1,8 @@ +import { defineApp } from "convex/server"; +import myComponent from "../component/convex.config.ts"; + +const app = defineApp(); + +// app.use(myComponent); + +export default app; \ No newline at end of file diff --git a/convex/schema.ts b/convex/schema.ts index c5e8879..a54ac0a 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -3,23 +3,23 @@ import { v } from "convex/values"; import { authTables } from "@convex-dev/auth/server"; // Define a messages table with an index. export default defineSchema({ - abcv: defineTable({ - title: v.string(), - }), - xiong: defineTable({ - name: v.string(), - title: v.string(), - }), - users: defineTable({ - id: v.string(), // 外部系统的用户 ID - name: v.string(), - createdAt: v.string(), - lastLoginAt: v.optional(v.string()), - }).index("id", ["id"]), - sessions: defineTable({ - userId: v.id("users"), - createdAt: v.string(), - expiresAt: v.optional(v.string()), - token: v.optional(v.string()), - }).index("token", ["token"]), + // abcv: defineTable({ + // title: v.string(), + // }), + // xiong: defineTable({ + // name: v.string(), + // title: v.string(), + // }), + // users: defineTable({ + // id: v.string(), // 外部系统的用户 ID + // name: v.string(), + // createdAt: v.string(), + // lastLoginAt: v.optional(v.string()), + // }).index("id", ["id"]), + // sessions: defineTable({ + // userId: v.id("users"), + // createdAt: v.string(), + // expiresAt: v.optional(v.string()), + // token: v.optional(v.string()), + // }).index("token", ["token"]), }); \ No newline at end of file diff --git a/convex/wrapper.ts b/convex/wrapper.ts new file mode 100644 index 0000000..0262ee9 --- /dev/null +++ b/convex/wrapper.ts @@ -0,0 +1,9 @@ +// import { query } from "./_generated/server"; +// import { components } from "./_generated/api"; + +// export const getTest2 = query({ +// args: {}, +// handler: async (ctx) => { +// return await ctx.runQuery(components.component.test2.get); +// }, +// }); diff --git a/convex/xiong.ts b/convex/xiong.ts index fb1fe91..3a029a6 100644 --- a/convex/xiong.ts +++ b/convex/xiong.ts @@ -1,8 +1,8 @@ -import { query, mutation, action } from "./_generated/server"; +// import { query, mutation, action } from "./_generated/server"; -export const get = query({ - args: {}, - handler: async (ctx) => { - return await ctx.db.query("xiong").collect(); - }, -}); \ No newline at end of file +// export const get = query({ +// args: {}, +// handler: async (ctx) => { +// return await ctx.db.query("xiong").collect(); +// }, +// }); \ No newline at end of file diff --git a/index.ts b/index.ts index 35ca8b5..2809e49 100644 --- a/index.ts +++ b/index.ts @@ -1,72 +1,10 @@ -import { ConvexClient, AuthTokenFetcher } from "convex/browser"; +import { ConvexClient } from "convex/browser"; import { api } from "./convex/_generated/api.js"; -import * as jose from "jose"; const url = process.env["CONVEX_URL"] const client = new ConvexClient(url!); -// 加载测试私钥 -const keys = JSON.parse(await Bun.file("./jwt/privateKey.json").text()); -const privateKey = await jose.importJWK(keys, "RS256"); +// 使用主应用的公共 API +const test2List = await client.query(api.wrapper.getTest2, {}); -// 生成 RS256 JWT -const payload = { - iss: "https://convex.kevisual.cn", - sub: "user:8fa2be73c2229e85", - aud: "convex-app", - exp: Math.floor(Date.now() / 1000) + 3600, - name: "Test User AA", - email: "test@example.com", -}; -const token = await new jose.SignJWT(payload) - .setProtectedHeader({ - "alg": "RS256", - "typ": "JWT", - "kid": "kid-key-1" - }) - .setIssuedAt() - .sign(privateKey); - -console.log("Generated RS256 token:", token); - -const authTokenFetcher: AuthTokenFetcher = async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => { - console.log("AuthTokenFetcher called, forceRefreshToken:", forceRefreshToken); - return token; -} -client.setAuth(authTokenFetcher, (isAuthenticated) => { - console.log("Auth isAuthenticated:", isAuthenticated); -}); -// console.log("Auth set up", auth); -await Bun.sleep(1000); -// const auth = client.getAuth(); -// console.log("Client auth", auth); -const unsubscribe = client.onUpdate(api.abcv.get, {}, async (tasks) => { - console.log(tasks); -}); - -const list = await client.query(api.abcv.get, {}); -console.log("Initial list:", list); - -// for (let i = 0; i < list.length; i++) { -// const a = list[i]; -// console.log(`Item ${i}:`, a.title); -// } -// const list = await client.action(api.abcv.chat, { message: "Hello, 1+1=?" }); -// console.log("Chat response:", list); -const xiongList = await client.query(api.xiong.get, {}); -console.log("Xiong list:", xiongList); -// const redisIsConnected = await client.action(api.actions.redis.isConnected, {}); -// console.log("Redis isConnected:", redisIsConnected); -// const sign = await api.auth.signIn({ provider: "convex" }) -// const sign = await httpClient.action("/auth", { method: "POST" }); -// console.log("Sign in result:", sign); - -const res = await fetch("http://convex.kevisual.cn:3211/auth", { - method: "POST", -}); - -const data = await res.json(); -console.log("Custom endpoint response:", data); -await Bun.sleep(1000); -unsubscribe(); -await client.close(); \ No newline at end of file +console.log("Test2 List:", test2List); \ No newline at end of file diff --git a/package.json b/package.json index ac306de..4a31f0c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "description": "", "main": "index.js", "scripts": { - "dev": "bunx convex dev" + "dev": "bunx convex dev", + "convex": "bunx convex dev" }, "keywords": [], "author": "abearxiong (https://www.xiongxiao.me)", @@ -26,4 +27,4 @@ "convex": "^1.31.6", "jose": "^6.1.3" } -} +} \ No newline at end of file diff --git a/src/test1.ts b/src/test1.ts new file mode 100644 index 0000000..b16de8c --- /dev/null +++ b/src/test1.ts @@ -0,0 +1,72 @@ +import { ConvexClient, AuthTokenFetcher } from "convex/browser"; +import { api } from "../convex/_generated/api.js"; +import * as jose from "jose"; + +const url = process.env["CONVEX_URL"] +const client = new ConvexClient(url!); + +// 加载测试私钥 +const keys = JSON.parse(await Bun.file("./jwt/privateKey.json").text()); +const privateKey = await jose.importJWK(keys, "RS256"); + +// 生成 RS256 JWT +const payload = { + iss: "https://convex.kevisual.cn", + sub: "user:8fa2be73c2229e85", + aud: "convex-app", + exp: Math.floor(Date.now() / 1000) + 3600, + name: "Test User AA", + email: "test@example.com", +}; +const token = await new jose.SignJWT(payload) + .setProtectedHeader({ + "alg": "RS256", + "typ": "JWT", + "kid": "kid-key-1" + }) + .setIssuedAt() + .sign(privateKey); + +console.log("Generated RS256 token:", token); + +const authTokenFetcher: AuthTokenFetcher = async ({ forceRefreshToken }: { forceRefreshToken: boolean }) => { + console.log("AuthTokenFetcher called, forceRefreshToken:", forceRefreshToken); + return token; +} +client.setAuth(authTokenFetcher, (isAuthenticated) => { + console.log("Auth isAuthenticated:", isAuthenticated); +}); +// console.log("Auth set up", auth); +await Bun.sleep(1000); +// const auth = client.getAuth(); +// console.log("Client auth", auth); +const unsubscribe = client.onUpdate(api.abcv.get, {}, async (tasks) => { + console.log(tasks); +}); + +const list = await client.query(api.abcv.get, {}); +console.log("Initial list:", list); + +// for (let i = 0; i < list.length; i++) { +// const a = list[i]; +// console.log(`Item ${i}:`, a.title); +// } +// const list = await client.action(api.abcv.chat, { message: "Hello, 1+1=?" }); +// console.log("Chat response:", list); +const xiongList = await client.query(api.xiong.get, {}); +console.log("Xiong list:", xiongList); +// const redisIsConnected = await client.action(api.actions.redis.isConnected, {}); +// console.log("Redis isConnected:", redisIsConnected); +// const sign = await api.auth.signIn({ provider: "convex" }) +// const sign = await httpClient.action("/auth", { method: "POST" }); +// console.log("Sign in result:", sign); + +const res = await fetch("http://convex.kevisual.cn:3211/auth", { + method: "POST", +}); + +const data = await res.json(); +console.log("Custom endpoint response:", data); +await Bun.sleep(1000); +unsubscribe(); +await client.close(); \ No newline at end of file