From 5df0fde9e7a67c5a880021d8894a332f1635f18d Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Tue, 14 May 2024 11:00:20 +0200 Subject: [PATCH 01/17] get started with whatsapp bot --- src/app.ts | 2 + src/routes/webhooks.ts | 8 +++ src/routes/webhooks/whatsapp.ts | 24 ++++++++ src/services/whatsapp.service.ts | 95 ++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 src/routes/webhooks.ts create mode 100644 src/routes/webhooks/whatsapp.ts create mode 100644 src/services/whatsapp.service.ts diff --git a/src/app.ts b/src/app.ts index d414aa7..f05a64b 100644 --- a/src/app.ts +++ b/src/app.ts @@ -6,6 +6,7 @@ import uiAuthRoute from "./routes/examples/auth"; import ragRoute from "./routes/rag"; import searchRoute from "./routes/search"; import mongoConnect from "./connections/mongodb.ts"; +import webhooksRoute from "./routes/webhooks.ts"; const app = new Hono(); if (Bun.env.NODE_ENV === "development") { @@ -19,5 +20,6 @@ app.route("/search", searchRoute); app.route("/knowledgebase", ragRoute); app.route("/auth/github", githubAuth); app.route("/examples/auth", uiAuthRoute); +app.route("/webhooks", webhooksRoute); export default app; diff --git a/src/routes/webhooks.ts b/src/routes/webhooks.ts new file mode 100644 index 0000000..dc9ad06 --- /dev/null +++ b/src/routes/webhooks.ts @@ -0,0 +1,8 @@ +import { Hono } from "hono"; +import whatsappRoute from "./webhooks/whatsapp.ts"; + +const app = new Hono(); + +app.route("whatsapp", whatsappRoute); + +export default app; diff --git a/src/routes/webhooks/whatsapp.ts b/src/routes/webhooks/whatsapp.ts new file mode 100644 index 0000000..7ac4b78 --- /dev/null +++ b/src/routes/webhooks/whatsapp.ts @@ -0,0 +1,24 @@ +import { Hono } from "hono"; +import { + handleWebhookMessage, + type WebhookMessage, +} from "../../services/whatsapp.service.ts"; + +const app = new Hono(); + +// TODO middleware to validate token +// TODO When and how do we attach the user and the users apps and permissions + +// validation endpoint which is needed +app.get("/", async (c) => { + const query = c.req.query(); + return c.text(query["hub.challenge"]); +}); + +app.post("/", async (c) => { + const body = (await c.req.json()) as WebhookMessage; + await handleWebhookMessage(body); + return c.json({}); +}); + +export default app; diff --git a/src/services/whatsapp.service.ts b/src/services/whatsapp.service.ts new file mode 100644 index 0000000..a8fd33f --- /dev/null +++ b/src/services/whatsapp.service.ts @@ -0,0 +1,95 @@ +interface WebhookMessage { + object: string; + entry: WebhookMessageEntry[]; +} + +interface WebhookMessageEntry { + id: string; + changes: WebhookChanges[]; +} + +interface WebhookChanges { + value: { + messaging_product: string; + metadata: { + display_phone_number: string; + phone_number_id: string; + }; + contacts: { profile: { name: string }; wa_id: string }[]; + messages: WebhookMessageReceive[]; + }; + field: string; +} + +interface WebhookMessageReceive { + from: string; + to: string; + timestamp: string; + text: { body: string }; + type: string; +} + +interface WebhookMessageSend { + messaging_product: "whatsapp"; + recipient_type: "individual"; + to: string; + type: "text"; + text: { + body: string; + }; +} + +const handleWebhookMessage = async (message: WebhookMessage): Promise => { + const { entry } = message; + + if ( + !Bun.env.WA_TOKEN || + !Bun.env.WA_BUSINESS_PHONE_NUMBER_ID || + entry[0].changes[0].field !== "messages" || + !entry[0].changes[0].value.messages + ) { + return; + } + + const phoneNumber = entry[0].changes[0].value.messages[0].from; + const messageText = entry[0].changes[0].value.messages[0].text.body; + console.log(messageText); + + const response = await fetch( + `https://graph.facebook.com/v19.0/${Bun.env.WA_BUSINESS_PHONE_NUMBER_ID}/messages`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${Bun.env.WA_TOKEN}`, + }, + body: JSON.stringify( + getTextMessage({ message: "Hello from Hono app", phoneNumber }), + ), + }, + ); + + console.log("response", response.status); + console.log("response", await response.json()); +}; + +function getTextMessage({ + message, + phoneNumber, +}: { + message: string; + phoneNumber: string; +}): WebhookMessageSend { + return { + messaging_product: "whatsapp", + recipient_type: "individual", + to: phoneNumber, + type: "text", + text: { + body: message, + }, + }; +} + +export { handleWebhookMessage }; +export type { WebhookMessage }; From a86bddfe3985e2e9ec3024def721e5fe982ba7db Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Tue, 14 May 2024 12:28:09 +0200 Subject: [PATCH 02/17] introduce telegram bot --- src/routes/webhooks.ts | 4 ++- src/routes/webhooks/telegram.ts | 24 +++++++++++++++ src/services/telegram.service.ts | 51 ++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 src/routes/webhooks/telegram.ts create mode 100644 src/services/telegram.service.ts diff --git a/src/routes/webhooks.ts b/src/routes/webhooks.ts index dc9ad06..08189bd 100644 --- a/src/routes/webhooks.ts +++ b/src/routes/webhooks.ts @@ -1,8 +1,10 @@ import { Hono } from "hono"; import whatsappRoute from "./webhooks/whatsapp.ts"; +import telegramRoute from "./webhooks/telegram.ts"; const app = new Hono(); -app.route("whatsapp", whatsappRoute); +app.route("/whatsapp", whatsappRoute); +app.route("/telegram", telegramRoute); export default app; diff --git a/src/routes/webhooks/telegram.ts b/src/routes/webhooks/telegram.ts new file mode 100644 index 0000000..01e092c --- /dev/null +++ b/src/routes/webhooks/telegram.ts @@ -0,0 +1,24 @@ +import { Hono } from "hono"; +import { + handleWebhookMessage, + type TelegramRequest, +} from "../../services/telegram.service.ts"; + +const app = new Hono(); + +// validation endpoint which is needed +app.get("/", async (c) => { + const query = c.req.query(); + console.log("query", query); + return c.text("true"); +}); + +app.post("/", async (c) => { + const body = (await c.req.json()) as TelegramRequest; + + await handleWebhookMessage(body); + + return c.json({}); +}); + +export default app; diff --git a/src/services/telegram.service.ts b/src/services/telegram.service.ts new file mode 100644 index 0000000..71cce8e --- /dev/null +++ b/src/services/telegram.service.ts @@ -0,0 +1,51 @@ +const telegramBaseUrl = `https://api.telegram.org/bot${Bun.env.TELEGRAM_TOKEN}`; + +interface TelegramRequest { + update_id: number; + message: TelegramMessage; +} + +interface TelegramMessage { + message_id: number; + from: { + id: number; + is_bot: false; + first_name: string; + last_name: string; + language_code: string; + }; + chat: { + id: number; + first_name: string; + last_name: string; + type: string; + }; + date: number; + text: string; +} + +const handleWebhookMessage = async ( + telegramRequestBody: TelegramRequest, +): Promise => { + const { message } = telegramRequestBody; + + if (!Bun.env.TELEGRAM_TOKEN) { + return; + } + const result = await fetch(`${telegramBaseUrl}/sendMessage`, { + method: "POST", + headers: { + "Content-type": "application/json", + }, + body: JSON.stringify({ + chat_id: message.chat.id, + text: "Hello from Hono app", + }), + }); + + console.log(result.status); + console.log(await result.json()); +}; + +export { handleWebhookMessage }; +export type { TelegramRequest }; From a957d97a723be9a2236cfc4a4d22152f191ccb4c Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Tue, 14 May 2024 16:05:54 +0200 Subject: [PATCH 03/17] update userTelegramId method --- src/models/user.model.ts | 3 +- src/repositories/interfaces.ts | 7 ++++ src/repositories/mongodb/user.repository.ts | 14 +++++++ src/routes/auth/telegram.ts | 43 +++++++++++++++++++++ src/services/user.service.ts | 25 ++++++++++++ src/utils/const.ts | 1 + 6 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 src/routes/auth/telegram.ts create mode 100644 src/services/user.service.ts diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 8ce3e2f..3f97276 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -14,6 +14,7 @@ export const UserSchema = new Schema({ applications: { type: Array, default: [] }, lastProvider: { type: String, default: "" }, lastUse: { type: Date, default: Date.now }, + telegramId: { type: Number, required: false }, }); export type User = { @@ -22,7 +23,7 @@ export type User = { applications: Application[]; lastProvider: string; lastUse: Date; -} +}; const User = mongoose.model("User", UserSchema); export default User; diff --git a/src/repositories/interfaces.ts b/src/repositories/interfaces.ts index 0e7d036..8c70c9b 100644 --- a/src/repositories/interfaces.ts +++ b/src/repositories/interfaces.ts @@ -120,4 +120,11 @@ export interface IUserRepository { appName: string; }): Promise; updateUser(props: { provider: string; email: string }): Promise; + updateUserTelegramId({ + email, + telegramId, + }: { + email: string; + telegramId?: number; + }): Promise; } diff --git a/src/repositories/mongodb/user.repository.ts b/src/repositories/mongodb/user.repository.ts index a2a8e19..04f5b25 100644 --- a/src/repositories/mongodb/user.repository.ts +++ b/src/repositories/mongodb/user.repository.ts @@ -40,6 +40,20 @@ class UserRepository extends BaseRepository implements IUserRepository { { returnNewDocument: true }, ); } + + public async updateUserTelegramId({ + email, + telegramId, + }: { + email: string; + telegramId?: number; + }): Promise { + return this.userModel.findOneAndUpdate( + { email }, + { telegramId }, + { returnNewDocument: true }, + ); + } } export default UserRepository; diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts new file mode 100644 index 0000000..e7c0c0d --- /dev/null +++ b/src/routes/auth/telegram.ts @@ -0,0 +1,43 @@ +import { Hono } from "hono"; +import { HTTPException } from "hono/http-exception"; +import contextMiddleware from "../../middlewares/context.middleware.ts"; +import type Context from "../../middlewares/context.ts"; +import authMiddleware from "../../middlewares/auth.middleware.ts"; +import type { USER_TYPE } from "../../utils/auth-utils.ts"; +import { updateUserTelegramId } from "../../services/user.service.ts"; +import { ServiceError } from "../../utils/service-errors.ts"; + +const app = new Hono<{ + Variables: { + user: USER_TYPE; + context: Context; + }; +}>(); +app.use(authMiddleware); +app.use(contextMiddleware); + +app.patch("/", async (c) => { + try { + const body = await c.req.json(); + + await updateUserTelegramId({ + telegramId: body.telegramId, + email: c.get("user").email, + context: c.get("context"), + }); + return c.json({}); + } catch (e: any) { + if (e instanceof HTTPException) { + throw e; + } else if (e instanceof ServiceError) { + throw new HTTPException(400, { message: e.explicitMessage }); + } else { + console.log(e.message); + throw new HTTPException(500, { + message: "Unknown error occured", + }); + } + } +}); + +export default app; diff --git a/src/services/user.service.ts b/src/services/user.service.ts new file mode 100644 index 0000000..2ba64d7 --- /dev/null +++ b/src/services/user.service.ts @@ -0,0 +1,25 @@ +import type { User } from "../models/user.model.ts"; +import type Context from "../middlewares/context.ts"; +import { httpError, USER_MONGO_DB_REPOSITORY } from "../utils/const.ts"; +import type { IUserRepository } from "../repositories/interfaces.ts"; +import { ServiceError } from "../utils/service-errors.ts"; + +const updateUserTelegramId = async ({ + telegramId, + context, + email, +}: { + telegramId?: number; + email: string; + context: Context; +}): Promise => { + const repository = context.get(USER_MONGO_DB_REPOSITORY); + const user = await repository.updateUserTelegramId({ email, telegramId }); + if (!user) { + throw new ServiceError(httpError.USER_NOT_FOUND); + } + + return user; +}; + +export { updateUserTelegramId }; diff --git a/src/utils/const.ts b/src/utils/const.ts index c4da420..2e08054 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -59,6 +59,7 @@ export const embeddingProvider: EmbeddingProvider = export const httpError = { USER_CANT_CREATE: "Couldn't create user", + USER_NOT_FOUND: "Can't find user", ENV_CANT_CREATE: "Couldn't create environment", APPNAME_LENGTH: `Application name length must be greater than ${APPNAME_MIN_LENGTH}`, APPNAME_NOT_ALLOWED: From b08cc71e011eec7abeec670a727a2f4f007e69ab Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Tue, 14 May 2024 16:11:57 +0200 Subject: [PATCH 04/17] add TODO --- src/routes/auth/telegram.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts index e7c0c0d..20aa737 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/auth/telegram.ts @@ -16,6 +16,7 @@ const app = new Hono<{ app.use(authMiddleware); app.use(contextMiddleware); +// TODO cover with e2e tests app.patch("/", async (c) => { try { const body = await c.req.json(); @@ -25,7 +26,7 @@ app.patch("/", async (c) => { email: c.get("user").email, context: c.get("context"), }); - return c.json({}); + return c.json({ done: true }); } catch (e: any) { if (e instanceof HTTPException) { throw e; From 333709108fd985f284cfe13ebaa8e5151c57bd3a Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Wed, 15 May 2024 13:38:06 +0200 Subject: [PATCH 05/17] create telegram route to update users telegramId --- src/app.ts | 6 ++++++ src/routes/auth/telegram.ts | 4 ++-- src/routes/examples/auth.ts | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app.ts b/src/app.ts index f05a64b..6cc9146 100644 --- a/src/app.ts +++ b/src/app.ts @@ -7,6 +7,7 @@ import ragRoute from "./routes/rag"; import searchRoute from "./routes/search"; import mongoConnect from "./connections/mongodb.ts"; import webhooksRoute from "./routes/webhooks.ts"; +import { cors } from "hono/cors"; const app = new Hono(); if (Bun.env.NODE_ENV === "development") { @@ -14,6 +15,11 @@ if (Bun.env.NODE_ENV === "development") { } await mongoConnect(); +app.use( + cors({ + origin: "https://5806-109-92-19-84.ngrok-free.app", + }), +); app.route("/apps", appsRoute); app.route("/search", searchRoute); diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts index 20aa737..efa8735 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/auth/telegram.ts @@ -1,11 +1,11 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; import type Context from "../../middlewares/context.ts"; -import authMiddleware from "../../middlewares/auth.middleware.ts"; import type { USER_TYPE } from "../../utils/auth-utils.ts"; import { updateUserTelegramId } from "../../services/user.service.ts"; import { ServiceError } from "../../utils/service-errors.ts"; +import authMiddleware from "../../middlewares/auth.middleware.ts"; +import contextMiddleware from "../../middlewares/context.middleware.ts"; const app = new Hono<{ Variables: { diff --git a/src/routes/examples/auth.ts b/src/routes/examples/auth.ts index c538ed0..c22fdd6 100644 --- a/src/routes/examples/auth.ts +++ b/src/routes/examples/auth.ts @@ -2,6 +2,7 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; import { getGithubLoginUrl } from "../../services/auth.service"; import { Layout } from "./components/Layout"; +import telegramRoute from "../auth/telegram.ts"; const app = new Hono(); @@ -18,4 +19,6 @@ app.get("/", (c) => { return c.html(Layout({ githubLoginUrl, googleLoginUrl: "" })); }); +app.route("/telegram", telegramRoute); + export default app; From 82b7e834e0c3af123c89fe90e098609ed6dad4a5 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Wed, 15 May 2024 14:22:08 +0200 Subject: [PATCH 06/17] use context middleware only once. create typings.d.ts for global types --- bun.lockb | Bin 52842 -> 60271 bytes package.json | 2 ++ src/app.ts | 11 ++++++++++- src/routes/applications.ts | 2 -- src/routes/auth/github.ts | 10 +--------- src/routes/auth/google.ts | 9 +-------- src/routes/auth/telegram.ts | 4 ---- src/routes/entities.ts | 5 ----- src/routes/environments.ts | 4 ---- src/routes/examples/auth.ts | 2 ++ src/routes/rag.ts | 4 ---- src/routes/search.ts | 4 ---- src/routes/users.ts | 19 +++++++++++++++++++ src/typings.d.ts | 7 +++++++ 14 files changed, 42 insertions(+), 41 deletions(-) create mode 100644 src/routes/users.ts create mode 100644 src/typings.d.ts diff --git a/bun.lockb b/bun.lockb index ec83e5d7e567e2aa629423620f175f4ff90b7da2..6ebdcd9d5c4eecf6827df495a83dbfa571baf326 100755 GIT binary patch delta 11490 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 diff --git a/package.json b/package.json index 3aa7ae0..48effd6 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.20.6", + "@clerk/backend": "^1.1.4", + "@hono/clerk-auth": "^2.0.0", "@langchain/openai": "^0.0.28", "assert": "^2.1.0", "axios": "^1.6.8", diff --git a/src/app.ts b/src/app.ts index 6cc9146..a5a8daf 100644 --- a/src/app.ts +++ b/src/app.ts @@ -8,6 +8,9 @@ import searchRoute from "./routes/search"; import mongoConnect from "./connections/mongodb.ts"; import webhooksRoute from "./routes/webhooks.ts"; import { cors } from "hono/cors"; +import userRoutes from "./routes/users.ts"; +import { clerkMiddleware } from "@hono/clerk-auth"; +import contextMiddleware from "./middlewares/context.middleware.ts"; const app = new Hono(); if (Bun.env.NODE_ENV === "development") { @@ -15,9 +18,14 @@ if (Bun.env.NODE_ENV === "development") { } await mongoConnect(); +app.use("*", clerkMiddleware()); +app.use(contextMiddleware); app.use( cors({ - origin: "https://5806-109-92-19-84.ngrok-free.app", + origin: [ + "https://5806-109-92-19-84.ngrok-free.app", + "http://localhost:5173", + ], }), ); @@ -27,5 +35,6 @@ app.route("/knowledgebase", ragRoute); app.route("/auth/github", githubAuth); app.route("/examples/auth", uiAuthRoute); app.route("/webhooks", webhooksRoute); +app.route("/users", userRoutes); export default app; diff --git a/src/routes/applications.ts b/src/routes/applications.ts index f765ab1..0aaaf66 100644 --- a/src/routes/applications.ts +++ b/src/routes/applications.ts @@ -12,7 +12,6 @@ import type { USER_TYPE } from "../utils/auth-utils"; import { APPNAME_MIN_LENGTH, APPNAME_REGEX, httpError } from "../utils/const"; import { ServiceError } from "../utils/service-errors"; import envsRoute from "./environments"; -import contextMiddleware from "../middlewares/context.middleware.ts"; import type Context from "../middlewares/context.ts"; const app = new Hono<{ @@ -22,7 +21,6 @@ const app = new Hono<{ }; }>(); app.use(authMiddleware); -app.use(contextMiddleware); app.get("/all", async (c) => { const user = c.get("user"); diff --git a/src/routes/auth/github.ts b/src/routes/auth/github.ts index a92f81e..2decb8a 100644 --- a/src/routes/auth/github.ts +++ b/src/routes/auth/github.ts @@ -4,16 +4,8 @@ import { sign as jwt_sign } from "hono/jwt"; import { finalizeAuth, getGithubUserData } from "../../services/auth.service"; import type { USER_TYPE } from "../../utils/auth-utils"; import { PROVIDER_GITHUB } from "../../utils/const"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; -import type Context from "../../middlewares/context.ts"; -const app = new Hono<{ - Variables: { - context: Context; - }; -}>(); - -app.use(contextMiddleware); +const app = new Hono(); app.get("/", async (c) => { const jwtSecret = Bun.env.JWT_SECRET; diff --git a/src/routes/auth/google.ts b/src/routes/auth/google.ts index b7db69a..ac9d02d 100644 --- a/src/routes/auth/google.ts +++ b/src/routes/auth/google.ts @@ -5,15 +5,8 @@ import { getGoogleLoginUrl, getGoogleUserData, } from "../../services/auth.service"; -import type Context from "../../middlewares/context.ts"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; -const app = new Hono<{ - Variables: { - context: Context; - }; -}>(); -app.use(contextMiddleware); +const app = new Hono(); app.get("/:db", async (c) => { const redirectUrl = c.req.query("redirectUrl") || Bun.env.GOOGLE_REDIRECT_URI; diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts index efa8735..b351217 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/auth/telegram.ts @@ -1,20 +1,16 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; -import type Context from "../../middlewares/context.ts"; import type { USER_TYPE } from "../../utils/auth-utils.ts"; import { updateUserTelegramId } from "../../services/user.service.ts"; import { ServiceError } from "../../utils/service-errors.ts"; import authMiddleware from "../../middlewares/auth.middleware.ts"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; - context: Context; }; }>(); app.use(authMiddleware); -app.use(contextMiddleware); // TODO cover with e2e tests app.patch("/", async (c) => { diff --git a/src/routes/entities.ts b/src/routes/entities.ts index d5b161b..cfa1f29 100644 --- a/src/routes/entities.ts +++ b/src/routes/entities.ts @@ -22,21 +22,16 @@ import { import { entityQueryValidator } from "../utils/route-validators"; import { RoutingError, ServiceError } from "../utils/service-errors"; import type { EntityRequestDto, PostEntityRequestDto } from "../utils/types.ts"; -import type Context from "../middlewares/context.ts"; -import contextMiddleware from "../middlewares/context.middleware.ts"; const app = new Hono< { Variables: { user: USER_TYPE; - context: Context; }; }, BlankSchema, "/:appName/:envName/:entityName" >(); -app.use(contextMiddleware); - app.get("/*", entityQueryValidator(), async (c) => { const q = c.req.valid("query"); const { xpath, pathRestSegments, xpathEntitySegments } = diff --git a/src/routes/environments.ts b/src/routes/environments.ts index fe2fdac..4c49b46 100644 --- a/src/routes/environments.ts +++ b/src/routes/environments.ts @@ -12,17 +12,13 @@ import { httpError } from "../utils/const"; import { asyncTryJson } from "../utils/route-utils"; import { ServiceError } from "../utils/service-errors"; import entitiesRoute from "./entities"; -import contextMiddleware from "../middlewares/context.middleware.ts"; -import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; - context: Context; }; }>(); app.use(authMiddleware); -app.use(contextMiddleware); app.get("/", async (c) => { const { appName, envName } = c.req.param() as { diff --git a/src/routes/examples/auth.ts b/src/routes/examples/auth.ts index c22fdd6..d47f0ac 100644 --- a/src/routes/examples/auth.ts +++ b/src/routes/examples/auth.ts @@ -19,6 +19,8 @@ app.get("/", (c) => { return c.html(Layout({ githubLoginUrl, googleLoginUrl: "" })); }); +app.post("/", async (c) => {}); + app.route("/telegram", telegramRoute); export default app; diff --git a/src/routes/rag.ts b/src/routes/rag.ts index 31ebcbb..8f7e4cd 100644 --- a/src/routes/rag.ts +++ b/src/routes/rag.ts @@ -5,17 +5,13 @@ import authMiddleware from "../middlewares/auth.middleware"; import { searchAiEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { ServiceError } from "../utils/service-errors"; -import contextMiddleware from "../middlewares/context.middleware.ts"; -import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; - context: Context; }; }>(); app.use(authMiddleware); -app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/search.ts b/src/routes/search.ts index 5f5191c..efbcf75 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -5,17 +5,13 @@ import authMiddleware from "../middlewares/auth.middleware"; import { searchEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { httpError } from "../utils/const"; -import contextMiddleware from "../middlewares/context.middleware.ts"; -import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; - context: Context; }; }>(); app.use(authMiddleware); -app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/users.ts b/src/routes/users.ts new file mode 100644 index 0000000..115bd38 --- /dev/null +++ b/src/routes/users.ts @@ -0,0 +1,19 @@ +import { Hono } from "hono"; +import { getAuth } from "@hono/clerk-auth"; + +const app = new Hono(); +app.get("/profile", async (c) => { + const clerkClient = c.get("clerk"); + const auth = getAuth(c); + + if (!auth?.userId) { + return c.json({ + message: "You are not logged in.", + }); + } + const user = await clerkClient.users.getUser(auth.userId); + console.log(user.primaryEmailAddress?.emailAddress); + return c.json(user); +}); + +export default app; diff --git a/src/typings.d.ts b/src/typings.d.ts new file mode 100644 index 0000000..09ca4e3 --- /dev/null +++ b/src/typings.d.ts @@ -0,0 +1,7 @@ +import type Context from "./middlewares/context.ts"; + +declare module "hono" { + interface ContextVariableMap { + context: Context; + } +} From f89c8bac3318e90cfb2ac226f2274f3d80824038 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Wed, 15 May 2024 14:45:07 +0200 Subject: [PATCH 07/17] Revert "use context middleware only once." This reverts commit 82b7e834e0c3af123c89fe90e098609ed6dad4a5. --- bun.lockb | Bin 60271 -> 52842 bytes package.json | 2 -- src/app.ts | 11 +---------- src/routes/applications.ts | 2 ++ src/routes/auth/github.ts | 10 +++++++++- src/routes/auth/google.ts | 9 ++++++++- src/routes/auth/telegram.ts | 4 ++++ src/routes/entities.ts | 5 +++++ src/routes/environments.ts | 4 ++++ src/routes/examples/auth.ts | 2 -- src/routes/rag.ts | 4 ++++ src/routes/search.ts | 4 ++++ src/routes/users.ts | 19 ------------------- src/typings.d.ts | 7 ------- 14 files changed, 41 insertions(+), 42 deletions(-) delete mode 100644 src/routes/users.ts delete mode 100644 src/typings.d.ts diff --git a/bun.lockb b/bun.lockb index 6ebdcd9d5c4eecf6827df495a83dbfa571baf326..ec83e5d7e567e2aa629423620f175f4ff90b7da2 100755 GIT binary patch delta 7006 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 delta 11490 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 app.use(authMiddleware); +app.use(contextMiddleware); app.get("/all", async (c) => { const user = c.get("user"); diff --git a/src/routes/auth/github.ts b/src/routes/auth/github.ts index 2decb8a..a92f81e 100644 --- a/src/routes/auth/github.ts +++ b/src/routes/auth/github.ts @@ -4,8 +4,16 @@ import { sign as jwt_sign } from "hono/jwt"; import { finalizeAuth, getGithubUserData } from "../../services/auth.service"; import type { USER_TYPE } from "../../utils/auth-utils"; import { PROVIDER_GITHUB } from "../../utils/const"; +import contextMiddleware from "../../middlewares/context.middleware.ts"; +import type Context from "../../middlewares/context.ts"; -const app = new Hono(); +const app = new Hono<{ + Variables: { + context: Context; + }; +}>(); + +app.use(contextMiddleware); app.get("/", async (c) => { const jwtSecret = Bun.env.JWT_SECRET; diff --git a/src/routes/auth/google.ts b/src/routes/auth/google.ts index ac9d02d..b7db69a 100644 --- a/src/routes/auth/google.ts +++ b/src/routes/auth/google.ts @@ -5,8 +5,15 @@ import { getGoogleLoginUrl, getGoogleUserData, } from "../../services/auth.service"; +import type Context from "../../middlewares/context.ts"; +import contextMiddleware from "../../middlewares/context.middleware.ts"; -const app = new Hono(); +const app = new Hono<{ + Variables: { + context: Context; + }; +}>(); +app.use(contextMiddleware); app.get("/:db", async (c) => { const redirectUrl = c.req.query("redirectUrl") || Bun.env.GOOGLE_REDIRECT_URI; diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts index b351217..efa8735 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/auth/telegram.ts @@ -1,16 +1,20 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; +import type Context from "../../middlewares/context.ts"; import type { USER_TYPE } from "../../utils/auth-utils.ts"; import { updateUserTelegramId } from "../../services/user.service.ts"; import { ServiceError } from "../../utils/service-errors.ts"; import authMiddleware from "../../middlewares/auth.middleware.ts"; +import contextMiddleware from "../../middlewares/context.middleware.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; + context: Context; }; }>(); app.use(authMiddleware); +app.use(contextMiddleware); // TODO cover with e2e tests app.patch("/", async (c) => { diff --git a/src/routes/entities.ts b/src/routes/entities.ts index cfa1f29..d5b161b 100644 --- a/src/routes/entities.ts +++ b/src/routes/entities.ts @@ -22,16 +22,21 @@ import { import { entityQueryValidator } from "../utils/route-validators"; import { RoutingError, ServiceError } from "../utils/service-errors"; import type { EntityRequestDto, PostEntityRequestDto } from "../utils/types.ts"; +import type Context from "../middlewares/context.ts"; +import contextMiddleware from "../middlewares/context.middleware.ts"; const app = new Hono< { Variables: { user: USER_TYPE; + context: Context; }; }, BlankSchema, "/:appName/:envName/:entityName" >(); +app.use(contextMiddleware); + app.get("/*", entityQueryValidator(), async (c) => { const q = c.req.valid("query"); const { xpath, pathRestSegments, xpathEntitySegments } = diff --git a/src/routes/environments.ts b/src/routes/environments.ts index 4c49b46..fe2fdac 100644 --- a/src/routes/environments.ts +++ b/src/routes/environments.ts @@ -12,13 +12,17 @@ import { httpError } from "../utils/const"; import { asyncTryJson } from "../utils/route-utils"; import { ServiceError } from "../utils/service-errors"; import entitiesRoute from "./entities"; +import contextMiddleware from "../middlewares/context.middleware.ts"; +import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; + context: Context; }; }>(); app.use(authMiddleware); +app.use(contextMiddleware); app.get("/", async (c) => { const { appName, envName } = c.req.param() as { diff --git a/src/routes/examples/auth.ts b/src/routes/examples/auth.ts index d47f0ac..c22fdd6 100644 --- a/src/routes/examples/auth.ts +++ b/src/routes/examples/auth.ts @@ -19,8 +19,6 @@ app.get("/", (c) => { return c.html(Layout({ githubLoginUrl, googleLoginUrl: "" })); }); -app.post("/", async (c) => {}); - app.route("/telegram", telegramRoute); export default app; diff --git a/src/routes/rag.ts b/src/routes/rag.ts index 8f7e4cd..31ebcbb 100644 --- a/src/routes/rag.ts +++ b/src/routes/rag.ts @@ -5,13 +5,17 @@ import authMiddleware from "../middlewares/auth.middleware"; import { searchAiEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { ServiceError } from "../utils/service-errors"; +import contextMiddleware from "../middlewares/context.middleware.ts"; +import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; + context: Context; }; }>(); app.use(authMiddleware); +app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/search.ts b/src/routes/search.ts index efbcf75..5f5191c 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -5,13 +5,17 @@ import authMiddleware from "../middlewares/auth.middleware"; import { searchEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { httpError } from "../utils/const"; +import contextMiddleware from "../middlewares/context.middleware.ts"; +import type Context from "../middlewares/context.ts"; const app = new Hono<{ Variables: { user: USER_TYPE; + context: Context; }; }>(); app.use(authMiddleware); +app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/users.ts b/src/routes/users.ts deleted file mode 100644 index 115bd38..0000000 --- a/src/routes/users.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Hono } from "hono"; -import { getAuth } from "@hono/clerk-auth"; - -const app = new Hono(); -app.get("/profile", async (c) => { - const clerkClient = c.get("clerk"); - const auth = getAuth(c); - - if (!auth?.userId) { - return c.json({ - message: "You are not logged in.", - }); - } - const user = await clerkClient.users.getUser(auth.userId); - console.log(user.primaryEmailAddress?.emailAddress); - return c.json(user); -}); - -export default app; diff --git a/src/typings.d.ts b/src/typings.d.ts deleted file mode 100644 index 09ca4e3..0000000 --- a/src/typings.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type Context from "./middlewares/context.ts"; - -declare module "hono" { - interface ContextVariableMap { - context: Context; - } -} From 6d73a284858ff70632467ecd8d2051426bfc7d7b Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Wed, 15 May 2024 15:03:03 +0200 Subject: [PATCH 08/17] add clerk packages --- bun.lockb | Bin 52842 -> 60271 bytes package.json | 2 ++ 2 files changed, 2 insertions(+) diff --git a/bun.lockb b/bun.lockb index ec83e5d7e567e2aa629423620f175f4ff90b7da2..d7740af34dbd6588b81a29f45a583f91c4885133 100755 GIT binary patch delta 11490 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 XH3LfjngMhAvnxB>SI<9i&fvcQPdVco delta 7006 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 diff --git a/package.json b/package.json index 3aa7ae0..f74c969 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.20.6", + "@clerk/backend": "^1.1.5", + "@hono/clerk-auth": "^2.0.0", "@langchain/openai": "^0.0.28", "assert": "^2.1.0", "axios": "^1.6.8", From 70870c5d2336565818d6fa45648956996db3de49 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Wed, 15 May 2024 15:56:58 +0200 Subject: [PATCH 09/17] WIP with middlewares --- src/app.ts | 17 ++++-- src/repositories/interfaces.ts | 6 +-- src/repositories/mongodb/user.repository.ts | 4 ++ src/routes/applications.ts | 5 -- src/routes/auth/telegram.ts | 4 -- src/routes/environments.ts | 4 -- src/routes/rag.ts | 4 -- src/routes/search.ts | 4 -- src/routes/users.ts | 47 ++++++++++++++++ src/routes/users/profile.ts | 19 +++++++ src/services/user.service.ts | 60 ++++++++++++++++++++- src/utils/const.ts | 2 + 12 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 src/routes/users.ts create mode 100644 src/routes/users/profile.ts diff --git a/src/app.ts b/src/app.ts index 6cc9146..e10af92 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,13 +1,15 @@ import { Hono } from "hono"; import { logger } from "hono/logger"; import appsRoute from "./routes/applications"; -import githubAuth from "./routes/auth/github"; -import uiAuthRoute from "./routes/examples/auth"; import ragRoute from "./routes/rag"; import searchRoute from "./routes/search"; import mongoConnect from "./connections/mongodb.ts"; import webhooksRoute from "./routes/webhooks.ts"; import { cors } from "hono/cors"; +import users from "./routes/users.ts"; +import { clerkMiddleware } from "@hono/clerk-auth"; +import authMiddleware from "./middlewares/auth.middleware.ts"; +import contextMiddleware from "./middlewares/context.middleware.ts"; const app = new Hono(); if (Bun.env.NODE_ENV === "development") { @@ -15,17 +17,22 @@ if (Bun.env.NODE_ENV === "development") { } await mongoConnect(); +app.use(contextMiddleware); app.use( cors({ origin: "https://5806-109-92-19-84.ngrok-free.app", }), ); +app.route("/webhooks", webhooksRoute); + +app.use(clerkMiddleware()); +app.route("/users", users); + +app.use(authMiddleware); +// TODO auth middleware could be included here app.route("/apps", appsRoute); app.route("/search", searchRoute); app.route("/knowledgebase", ragRoute); -app.route("/auth/github", githubAuth); -app.route("/examples/auth", uiAuthRoute); -app.route("/webhooks", webhooksRoute); export default app; diff --git a/src/repositories/interfaces.ts b/src/repositories/interfaces.ts index 8c70c9b..25845e3 100644 --- a/src/repositories/interfaces.ts +++ b/src/repositories/interfaces.ts @@ -120,11 +120,9 @@ export interface IUserRepository { appName: string; }): Promise; updateUser(props: { provider: string; email: string }): Promise; - updateUserTelegramId({ - email, - telegramId, - }: { + updateUserTelegramId(props: { email: string; telegramId?: number; }): Promise; + findUserByEmail(email: string): Promise; } diff --git a/src/repositories/mongodb/user.repository.ts b/src/repositories/mongodb/user.repository.ts index 04f5b25..cc99c79 100644 --- a/src/repositories/mongodb/user.repository.ts +++ b/src/repositories/mongodb/user.repository.ts @@ -54,6 +54,10 @@ class UserRepository extends BaseRepository implements IUserRepository { { returnNewDocument: true }, ); } + + public async findUserByEmail(email: string): Promise { + return this.userModel.findOne({ email }); + } } export default UserRepository; diff --git a/src/routes/applications.ts b/src/routes/applications.ts index f765ab1..4c2e309 100644 --- a/src/routes/applications.ts +++ b/src/routes/applications.ts @@ -1,6 +1,5 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; -import authMiddleware from "../middlewares/auth.middleware"; import { createApplication, deleteApplication, @@ -12,7 +11,6 @@ import type { USER_TYPE } from "../utils/auth-utils"; import { APPNAME_MIN_LENGTH, APPNAME_REGEX, httpError } from "../utils/const"; import { ServiceError } from "../utils/service-errors"; import envsRoute from "./environments"; -import contextMiddleware from "../middlewares/context.middleware.ts"; import type Context from "../middlewares/context.ts"; const app = new Hono<{ @@ -21,12 +19,9 @@ const app = new Hono<{ context: Context; }; }>(); -app.use(authMiddleware); -app.use(contextMiddleware); app.get("/all", async (c) => { const user = c.get("user"); - const apps = await getUserApplications({ context: c.get("context"), userEmail: user.email, diff --git a/src/routes/auth/telegram.ts b/src/routes/auth/telegram.ts index efa8735..38f899e 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/auth/telegram.ts @@ -4,8 +4,6 @@ import type Context from "../../middlewares/context.ts"; import type { USER_TYPE } from "../../utils/auth-utils.ts"; import { updateUserTelegramId } from "../../services/user.service.ts"; import { ServiceError } from "../../utils/service-errors.ts"; -import authMiddleware from "../../middlewares/auth.middleware.ts"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; const app = new Hono<{ Variables: { @@ -13,8 +11,6 @@ const app = new Hono<{ context: Context; }; }>(); -app.use(authMiddleware); -app.use(contextMiddleware); // TODO cover with e2e tests app.patch("/", async (c) => { diff --git a/src/routes/environments.ts b/src/routes/environments.ts index fe2fdac..38add55 100644 --- a/src/routes/environments.ts +++ b/src/routes/environments.ts @@ -1,6 +1,5 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; -import authMiddleware from "../middlewares/auth.middleware"; import { createEnvironment, deleteEnvironment, @@ -12,7 +11,6 @@ import { httpError } from "../utils/const"; import { asyncTryJson } from "../utils/route-utils"; import { ServiceError } from "../utils/service-errors"; import entitiesRoute from "./entities"; -import contextMiddleware from "../middlewares/context.middleware.ts"; import type Context from "../middlewares/context.ts"; const app = new Hono<{ @@ -21,8 +19,6 @@ const app = new Hono<{ context: Context; }; }>(); -app.use(authMiddleware); -app.use(contextMiddleware); app.get("/", async (c) => { const { appName, envName } = c.req.param() as { diff --git a/src/routes/rag.ts b/src/routes/rag.ts index 31ebcbb..06c50ea 100644 --- a/src/routes/rag.ts +++ b/src/routes/rag.ts @@ -1,11 +1,9 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; import * as R from "ramda"; -import authMiddleware from "../middlewares/auth.middleware"; import { searchAiEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { ServiceError } from "../utils/service-errors"; -import contextMiddleware from "../middlewares/context.middleware.ts"; import type Context from "../middlewares/context.ts"; const app = new Hono<{ @@ -14,8 +12,6 @@ const app = new Hono<{ context: Context; }; }>(); -app.use(authMiddleware); -app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/search.ts b/src/routes/search.ts index 5f5191c..e856f73 100644 --- a/src/routes/search.ts +++ b/src/routes/search.ts @@ -1,11 +1,9 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; import * as R from "ramda"; -import authMiddleware from "../middlewares/auth.middleware"; import { searchEntities } from "../services/entity.service"; import type { USER_TYPE } from "../utils/auth-utils"; import { httpError } from "../utils/const"; -import contextMiddleware from "../middlewares/context.middleware.ts"; import type Context from "../middlewares/context.ts"; const app = new Hono<{ @@ -14,8 +12,6 @@ const app = new Hono<{ context: Context; }; }>(); -app.use(authMiddleware); -app.use(contextMiddleware); app.post("/:app/:env/*", async (c) => { const { app, env } = c.req.param(); diff --git a/src/routes/users.ts b/src/routes/users.ts new file mode 100644 index 0000000..369cb19 --- /dev/null +++ b/src/routes/users.ts @@ -0,0 +1,47 @@ +import { Hono } from "hono"; +import { getAuth } from "@hono/clerk-auth"; +import type Context from "../middlewares/context.ts"; +import { type ClerkClient } from "@clerk/backend"; +import { HTTPException } from "hono/http-exception"; +import { httpError } from "../utils/const.ts"; +import { createOrFetchUser } from "../services/user.service.ts"; + +const app = new Hono<{ + Variables: { + context: Context; + clerk: ClerkClient; + }; +}>(); + +app.post("/auth", async (c) => { + const clerkClient = c.get("clerk"); + const auth = getAuth(c); + + if (!auth?.userId) { + throw new HTTPException(401, { + message: httpError.USER_NOT_AUTHENTICATED, + }); + } + + const clerkUser = await clerkClient.users.getUser(auth.userId); + if (!clerkUser) { + throw new HTTPException(401, { + message: httpError.USER_NOT_AUTHENTICATED, + }); + } + + try { + console.log(clerkUser.primaryEmailAddress?.emailAddress); + const user = await createOrFetchUser({ + user: clerkUser, + context: c.get("context"), + }); + return c.json(user); + } catch (e) { + throw new HTTPException(500, { + message: httpError.UNKNOWN, + }); + } +}); + +export default app; diff --git a/src/routes/users/profile.ts b/src/routes/users/profile.ts new file mode 100644 index 0000000..115bd38 --- /dev/null +++ b/src/routes/users/profile.ts @@ -0,0 +1,19 @@ +import { Hono } from "hono"; +import { getAuth } from "@hono/clerk-auth"; + +const app = new Hono(); +app.get("/profile", async (c) => { + const clerkClient = c.get("clerk"); + const auth = getAuth(c); + + if (!auth?.userId) { + return c.json({ + message: "You are not logged in.", + }); + } + const user = await clerkClient.users.getUser(auth.userId); + console.log(user.primaryEmailAddress?.emailAddress); + return c.json(user); +}); + +export default app; diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 2ba64d7..9eb4d27 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -1,8 +1,16 @@ import type { User } from "../models/user.model.ts"; import type Context from "../middlewares/context.ts"; -import { httpError, USER_MONGO_DB_REPOSITORY } from "../utils/const.ts"; +import { + defaultNodbEnv, + ENVIRONMENT_MONGO_DB_REPOSITORY, + httpError, + USER_MONGO_DB_REPOSITORY, +} from "../utils/const.ts"; import type { IUserRepository } from "../repositories/interfaces.ts"; import { ServiceError } from "../utils/service-errors.ts"; +import { type User as ClerkUser } from "@clerk/backend"; +import generateAppName from "../utils/app-name.ts"; +import { EnvironmentRepository } from "../repositories/mongodb"; const updateUserTelegramId = async ({ telegramId, @@ -22,4 +30,52 @@ const updateUserTelegramId = async ({ return user; }; -export { updateUserTelegramId }; +const findUserByEmail = async ({ + email, + context, +}: { + email: string; + context: Context; +}): Promise => { + const repository = context.get(USER_MONGO_DB_REPOSITORY); + return repository.findUserByEmail(email); +}; + +const createOrFetchUser = async ({ + user, + context, +}: { + user: ClerkUser; + context: Context; +}): Promise => { + const userEmail = user.primaryEmailAddress?.emailAddress; + + if (!userEmail) { + throw new ServiceError(httpError.USER_DOES_NOT_HAVE_EMAIL); + } + + const dbUser = await findUserByEmail({ email: userEmail, context }); + + if (dbUser) { + return dbUser; + } + const repository = context.get(USER_MONGO_DB_REPOSITORY); + const appName = generateAppName(); + const environmentRepository = context.get( + ENVIRONMENT_MONGO_DB_REPOSITORY, + ); + + await environmentRepository.createEnvironment({ + appName, + envName: defaultNodbEnv, + }); + + // TODO remove provider + return repository.createUser({ + provider: "github", + email: userEmail, + appName, + }); +}; + +export { updateUserTelegramId, createOrFetchUser, findUserByEmail }; diff --git a/src/utils/const.ts b/src/utils/const.ts index 2e08054..ec5d57b 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -60,6 +60,8 @@ export const embeddingProvider: EmbeddingProvider = export const httpError = { USER_CANT_CREATE: "Couldn't create user", USER_NOT_FOUND: "Can't find user", + USER_NOT_AUTHENTICATED: "Unauthenticated", + USER_DOES_NOT_HAVE_EMAIL: "User does not have a linked email", ENV_CANT_CREATE: "Couldn't create environment", APPNAME_LENGTH: `Application name length must be greater than ${APPNAME_MIN_LENGTH}`, APPNAME_NOT_ALLOWED: From 5f6b25d82dc348d7e37ed7547bd84ceaad8dd950 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Thu, 16 May 2024 11:28:45 +0200 Subject: [PATCH 10/17] WIP --- src/app.ts | 7 +++++-- src/routes/users.ts | 15 ++++++++++++++- src/routes/users/profile.ts | 19 ------------------- 3 files changed, 19 insertions(+), 22 deletions(-) delete mode 100644 src/routes/users/profile.ts diff --git a/src/app.ts b/src/app.ts index e10af92..532a194 100644 --- a/src/app.ts +++ b/src/app.ts @@ -8,8 +8,8 @@ import webhooksRoute from "./routes/webhooks.ts"; import { cors } from "hono/cors"; import users from "./routes/users.ts"; import { clerkMiddleware } from "@hono/clerk-auth"; -import authMiddleware from "./middlewares/auth.middleware.ts"; import contextMiddleware from "./middlewares/context.middleware.ts"; +import authMiddleware from "./middlewares/auth.middleware.ts"; const app = new Hono(); if (Bun.env.NODE_ENV === "development") { @@ -20,7 +20,10 @@ await mongoConnect(); app.use(contextMiddleware); app.use( cors({ - origin: "https://5806-109-92-19-84.ngrok-free.app", + origin: [ + "https://5806-109-92-19-84.ngrok-free.app", + "http://localhost:5173", + ], }), ); diff --git a/src/routes/users.ts b/src/routes/users.ts index 369cb19..ed5d789 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -31,7 +31,6 @@ app.post("/auth", async (c) => { } try { - console.log(clerkUser.primaryEmailAddress?.emailAddress); const user = await createOrFetchUser({ user: clerkUser, context: c.get("context"), @@ -44,4 +43,18 @@ app.post("/auth", async (c) => { } }); +app.get("/profile", async (c) => { + const clerkClient = c.get("clerk"); + const auth = getAuth(c); + + if (!auth?.userId) { + return c.json({ + message: "You are not logged in.", + }); + } + const user = await clerkClient.users.getUser(auth.userId); + + return c.json(user); +}); + export default app; diff --git a/src/routes/users/profile.ts b/src/routes/users/profile.ts deleted file mode 100644 index 115bd38..0000000 --- a/src/routes/users/profile.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Hono } from "hono"; -import { getAuth } from "@hono/clerk-auth"; - -const app = new Hono(); -app.get("/profile", async (c) => { - const clerkClient = c.get("clerk"); - const auth = getAuth(c); - - if (!auth?.userId) { - return c.json({ - message: "You are not logged in.", - }); - } - const user = await clerkClient.users.getUser(auth.userId); - console.log(user.primaryEmailAddress?.emailAddress); - return c.json(user); -}); - -export default app; From 4ae5b68954fe8d7d49702552ff3652aedc8bcdf8 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Thu, 16 May 2024 12:00:09 +0200 Subject: [PATCH 11/17] WIP --- src/middlewares/auth.middleware.ts | 35 +++-- src/models/user.model.ts | 6 +- src/repositories/interfaces.ts | 7 +- src/repositories/mongodb/user.repository.ts | 30 ++-- src/routes/auth/github.ts | 58 -------- src/routes/auth/google.ts | 61 -------- src/routes/examples/auth.ts | 24 --- src/routes/examples/components/Layout.tsx | 26 ---- src/routes/users.ts | 18 ++- src/routes/{auth => users}/telegram.ts | 2 +- src/services/auth.service.ts | 157 -------------------- src/services/user.service.ts | 32 +++- src/utils/auth-utils.ts | 8 +- 13 files changed, 87 insertions(+), 377 deletions(-) delete mode 100644 src/routes/auth/github.ts delete mode 100644 src/routes/auth/google.ts delete mode 100644 src/routes/examples/auth.ts delete mode 100644 src/routes/examples/components/Layout.tsx rename src/routes/{auth => users}/telegram.ts (95%) delete mode 100644 src/services/auth.service.ts diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index d376fea..4a3f2af 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -1,25 +1,32 @@ import { createFactory } from "hono/factory"; import { HTTPException } from "hono/http-exception"; -import { verify as jwt_verify } from "hono/jwt"; -import { bearerFromHeader } from "../utils/auth-utils"; +import { getAuth } from "@hono/clerk-auth"; +import { httpError } from "../utils/const.ts"; +import { findUserByClerkId } from "../services/user.service.ts"; const factory = createFactory(); const middleware = factory.createMiddleware(async (c, next) => { - const authorizationHeader = c.req.header("Authorization"); - const secret = Bun.env.JWT_SECRET; - if (!authorizationHeader || !secret) { - throw new HTTPException(401, { message: "Unauthorized request" }); + const clerkClient = c.get("clerk"); + const auth = getAuth(c); + + if (!auth?.userId) { + throw new HTTPException(401, { + message: httpError.USER_NOT_AUTHENTICATED, + }); } - const bearerToken = bearerFromHeader(authorizationHeader); - try { - const user = await jwt_verify(bearerToken, secret); - c.set("user", user); - await next(); - } catch (err) { - console.log({ err }); - throw new HTTPException(401, { message: "Unauthorized" }); + + const user = findUserByClerkId({ + id: auth.userId, + context: c.get("context"), + }); + + if (!user) { + throw new HTTPException(404, { message: httpError.USER_NOT_FOUND }); } + + c.set("user", user); + await next(); }); export default middleware; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 3f97276..9873f0c 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -10,19 +10,17 @@ export const UserSchema = new Schema({ unique: true, index: true, }, - providers: { type: Array, default: [] }, applications: { type: Array, default: [] }, - lastProvider: { type: String, default: "" }, + clerkUserId: { type: String, required: true, unique: true }, lastUse: { type: Date, default: Date.now }, telegramId: { type: Number, required: false }, }); export type User = { email: string; - providers: string[]; applications: Application[]; - lastProvider: string; lastUse: Date; + clerkUserId: string; }; const User = mongoose.model("User", UserSchema); diff --git a/src/repositories/interfaces.ts b/src/repositories/interfaces.ts index 25845e3..d710144 100644 --- a/src/repositories/interfaces.ts +++ b/src/repositories/interfaces.ts @@ -115,14 +115,15 @@ export interface IEntityRepository { export interface IUserRepository { createUser(props: { - provider: string; + clerkUserId: string; email: string; appName: string; }): Promise; - updateUser(props: { provider: string; email: string }): Promise; + updateUserLastUse(props: { clerkUserId: string }): Promise; updateUserTelegramId(props: { - email: string; + clerkUserId: string; telegramId?: number; }): Promise; findUserByEmail(email: string): Promise; + findUserClerkId(id: string): Promise; } diff --git a/src/repositories/mongodb/user.repository.ts b/src/repositories/mongodb/user.repository.ts index cc99c79..ca13b34 100644 --- a/src/repositories/mongodb/user.repository.ts +++ b/src/repositories/mongodb/user.repository.ts @@ -8,48 +8,44 @@ class UserRepository extends BaseRepository implements IUserRepository { } public async createUser({ - provider, + clerkUserId, email, appName, }: { - provider: string; + clerkUserId: string; email: string; appName: string; }): Promise { return this.userModel.create({ email, - providers: [provider], - lastProvider: provider, + clerkUserId, applications: [appName], }); } - public async updateUser({ - provider, - email, + public async updateUserLastUse({ + clerkUserId, }: { - provider: string; - email: string; + clerkUserId: string; }): Promise { return this.userModel.findOneAndUpdate( - { email }, + { clerkUserId }, { - $addToSet: { providers: provider }, - $set: { lastProvider: provider }, + lastUse: Date.now(), }, { returnNewDocument: true }, ); } public async updateUserTelegramId({ - email, + clerkUserId, telegramId, }: { - email: string; + clerkUserId: string; telegramId?: number; }): Promise { return this.userModel.findOneAndUpdate( - { email }, + { clerkUserId }, { telegramId }, { returnNewDocument: true }, ); @@ -58,6 +54,10 @@ class UserRepository extends BaseRepository implements IUserRepository { public async findUserByEmail(email: string): Promise { return this.userModel.findOne({ email }); } + + public async findUserClerkId(id: string): Promise { + return this.userModel.findOne({ clerkUserId: id }); + } } export default UserRepository; diff --git a/src/routes/auth/github.ts b/src/routes/auth/github.ts deleted file mode 100644 index a92f81e..0000000 --- a/src/routes/auth/github.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Hono } from "hono"; -import { HTTPException } from "hono/http-exception"; -import { sign as jwt_sign } from "hono/jwt"; -import { finalizeAuth, getGithubUserData } from "../../services/auth.service"; -import type { USER_TYPE } from "../../utils/auth-utils"; -import { PROVIDER_GITHUB } from "../../utils/const"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; -import type Context from "../../middlewares/context.ts"; - -const app = new Hono<{ - Variables: { - context: Context; - }; -}>(); - -app.use(contextMiddleware); - -app.get("/", async (c) => { - const jwtSecret = Bun.env.JWT_SECRET; - if (!jwtSecret) { - throw new HTTPException(400, { - message: "Env is missing", - }); - } - const code = c.req.query("code"); - const githubRedirectUrl = Bun.env.GITHUB_CALLBACK_URL; - if (!code || !githubRedirectUrl) { - throw new HTTPException(400, { - message: "Authorization code or redirect URL is missing in the request", - }); - } - try { - const userData: USER_TYPE = await getGithubUserData({ - redirectUrl: githubRedirectUrl, - code: code, - }); - if (!userData.email) { - throw new HTTPException(500, { - message: "Couldn't store user data", - }); - } - const jwtToken = await jwt_sign(userData, jwtSecret); - c.res.headers.set("Authorization", `Bearer ${jwtToken}`); - await finalizeAuth({ - context: c.get("context"), - email: userData.email, - provider: PROVIDER_GITHUB, - }); - return c.json({ userData }); - } catch (e: any) { - console.log(e.message); - throw new HTTPException(500, { - message: "Unknown error occured", - }); - } -}); - -export default app; diff --git a/src/routes/auth/google.ts b/src/routes/auth/google.ts deleted file mode 100644 index b7db69a..0000000 --- a/src/routes/auth/google.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Hono } from "hono"; -import { HTTPException } from "hono/http-exception"; -import { sign as jwt_sign } from "hono/jwt"; -import { - getGoogleLoginUrl, - getGoogleUserData, -} from "../../services/auth.service"; -import type Context from "../../middlewares/context.ts"; -import contextMiddleware from "../../middlewares/context.middleware.ts"; - -const app = new Hono<{ - Variables: { - context: Context; - }; -}>(); -app.use(contextMiddleware); - -app.get("/:db", async (c) => { - const redirectUrl = c.req.query("redirectUrl") || Bun.env.GOOGLE_REDIRECT_URI; - if (!redirectUrl) { - throw new HTTPException(400, { - message: "Redirect url missing in request", - }); - } - const loginUrl = getGoogleLoginUrl({ redirectUrl }); - return c.json({ loginUrl }); -}); - -app.post("/:db", async (c) => { - const { JWT_SECRET } = Bun.env; - if (!JWT_SECRET) { - throw new HTTPException(400, { - message: "Env is missing", - }); - } - const body = await c.req.json(); - if (!body.code || !body.redirectUrl) { - throw new HTTPException(400, { - message: "Authorization code or redirect URL is missing in the request", - }); - } - - try { - const userData = await getGoogleUserData({ - redirectUrl: body.redirectUrl, - code: body.code, - context: c.get("context"), - }); - const jwtToken = await jwt_sign(userData, JWT_SECRET); - c.res.headers.set("Authorization", `Bearer ${jwtToken}`); - return c.json({ - status: true, - message: "Login Success", - ...userData, - }); - } catch (error) { - throw new HTTPException(500, { - message: "Failed to set user data", - }); - } -}); diff --git a/src/routes/examples/auth.ts b/src/routes/examples/auth.ts deleted file mode 100644 index c22fdd6..0000000 --- a/src/routes/examples/auth.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Hono } from "hono"; -import { HTTPException } from "hono/http-exception"; -import { getGithubLoginUrl } from "../../services/auth.service"; -import { Layout } from "./components/Layout"; -import telegramRoute from "../auth/telegram.ts"; - -const app = new Hono(); - -app.get("/", (c) => { - const githubRedirectUrl = Bun.env.GITHUB_CALLBACK_URL; - if (!githubRedirectUrl) { - throw new HTTPException(400, { - message: "Missing Github redirect url", - }); - } - const githubLoginUrl = getGithubLoginUrl({ - redirectUrl: githubRedirectUrl, - }); - return c.html(Layout({ githubLoginUrl, googleLoginUrl: "" })); -}); - -app.route("/telegram", telegramRoute); - -export default app; diff --git a/src/routes/examples/components/Layout.tsx b/src/routes/examples/components/Layout.tsx deleted file mode 100644 index c9bee50..0000000 --- a/src/routes/examples/components/Layout.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { html } from "hono/html"; - -export const Layout = ({ - googleLoginUrl, - githubLoginUrl, -}: { - googleLoginUrl: string; - githubLoginUrl: string; -}) => { - return html` - - - - - Title - - - - - `; -}; diff --git a/src/routes/users.ts b/src/routes/users.ts index ed5d789..1e7f4d2 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -4,7 +4,12 @@ import type Context from "../middlewares/context.ts"; import { type ClerkClient } from "@clerk/backend"; import { HTTPException } from "hono/http-exception"; import { httpError } from "../utils/const.ts"; -import { createOrFetchUser } from "../services/user.service.ts"; +import { + createOrFetchUser, + findUserByEmail, +} from "../services/user.service.ts"; +import telegramRoute from "./users/telegram.ts"; +import authMiddleware from "../middlewares/auth.middleware.ts"; const app = new Hono<{ Variables: { @@ -13,6 +18,7 @@ const app = new Hono<{ }; }>(); +// TODO e2e tests app.post("/auth", async (c) => { const clerkClient = c.get("clerk"); const auth = getAuth(c); @@ -52,9 +58,17 @@ app.get("/profile", async (c) => { message: "You are not logged in.", }); } - const user = await clerkClient.users.getUser(auth.userId); + const clerkUser = await clerkClient.users.getUser(auth.userId); + + const user = await findUserByEmail({ + email: clerkUser.primaryEmailAddress?.emailAddress || "", + context: c.get("context"), + }); return c.json(user); }); +app.use(authMiddleware); +app.route("/settings/telegram", telegramRoute); + export default app; diff --git a/src/routes/auth/telegram.ts b/src/routes/users/telegram.ts similarity index 95% rename from src/routes/auth/telegram.ts rename to src/routes/users/telegram.ts index 38f899e..e5b4f07 100644 --- a/src/routes/auth/telegram.ts +++ b/src/routes/users/telegram.ts @@ -19,7 +19,7 @@ app.patch("/", async (c) => { await updateUserTelegramId({ telegramId: body.telegramId, - email: c.get("user").email, + clerkUserId: c.get("user").clerkUserId, context: c.get("context"), }); return c.json({ done: true }); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts deleted file mode 100644 index 0e77ee1..0000000 --- a/src/services/auth.service.ts +++ /dev/null @@ -1,157 +0,0 @@ -import axios from "axios"; -import { HTTPException } from "hono/http-exception"; -import { decode as jwt_decode } from "hono/jwt"; -import * as R from "ramda"; -import generateAppName from "../utils/app-name"; -import { generateState } from "../utils/auth-utils"; -import { - defaultNodbEnv, - ENVIRONMENT_MONGO_DB_REPOSITORY, - PROVIDER_GOOGLE, - USER_MONGO_DB_REPOSITORY, -} from "../utils/const"; -import type Context from "../middlewares/context.ts"; -import { EnvironmentRepository, UserRepository } from "../repositories/mongodb"; -import { type User } from "../models/user.model.ts"; - -export const getGithubLoginUrl = ({ redirectUrl }: { redirectUrl: string }) => { - const { GITHUB_CLIENT_ID, GITHUB_AUTH_ENDPOINT } = Bun.env; - if (!GITHUB_CLIENT_ID || !GITHUB_AUTH_ENDPOINT) { - throw new HTTPException(400, { - message: "Missing Github env", - }); - } - const params = new URLSearchParams(); - params.append("client_id", GITHUB_CLIENT_ID); - params.append("redirect_uri", redirectUrl); - params.append("scope", ["read:user", "user:email"].join(" ")); - params.append("allow_signup", "true"); - return `${process.env.GITHUB_AUTH_ENDPOINT}?${params.toString()}`; -}; - -export const getGoogleLoginUrl = ({ redirectUrl }: { redirectUrl: string }) => { - const { GOOGLE_RESPONSE_TYPE, GOOGLE_CLIENT_ID, GOOGLE_SCOPE } = Bun.env; - if (!GOOGLE_RESPONSE_TYPE || !GOOGLE_CLIENT_ID || !GOOGLE_SCOPE) { - throw new HTTPException(400, { - message: "Missing Google env", - }); - } - const state = generateState(); - const params = new URLSearchParams(); - params.append("client_id", GOOGLE_CLIENT_ID); - params.append("response_type", GOOGLE_RESPONSE_TYPE); - params.append("redirect_uri", redirectUrl); - params.append("scope", GOOGLE_SCOPE); - params.append("state", state); - return `${process.env.GOOGLE_AUTH_ENDPOINT}?${params.toString()}`; -}; - -export const finalizeAuth = async ({ - context, - email, - provider, -}: { - context: Context; - email: string; - provider: string; -}): Promise => { - const userRepository = context.get(USER_MONGO_DB_REPOSITORY); - const updatedUser = await userRepository.updateUser({ email, provider }); - - if (updatedUser) return updatedUser; - - const appName = generateAppName(); - const environmentRepository = context.get( - ENVIRONMENT_MONGO_DB_REPOSITORY, - ); - - await environmentRepository.createEnvironment({ - appName, - envName: defaultNodbEnv, - }); - - return userRepository.createUser({ provider, email, appName }); -}; - -export const getGoogleUserData = async ({ - redirectUrl, - code, - context, -}: { - redirectUrl: string; - code: string; - context: Context; -}) => { - const { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } = Bun.env; - if (!GOOGLE_CLIENT_ID || !GOOGLE_CLIENT_SECRET) { - throw new HTTPException(400, { - message: "Missing Google access env", - }); - } - const params = new URLSearchParams(); - params.append("grant_type", "authorization_code"); - params.append("code", decodeURIComponent(code)); - params.append("redirect_uri", redirectUrl); - params.append("client_id", GOOGLE_CLIENT_ID); - params.append("client_secret", GOOGLE_CLIENT_SECRET); - const options = { - method: "POST", - headers: { "content-type": "application/x-www-form-urlencoded" }, - data: params.toString(), - url: process.env.GOOGLE_TOKEN_ENDPOINT, - }; - const response = await axios(options); - if (response.status !== 200) { - throw new HTTPException(400, { - message: "Error while getting Google response", - }); - } - const { id_token } = response.data; - const email = R.path(["payload", "email"], jwt_decode(id_token)); - return finalizeAuth({ context, email, provider: PROVIDER_GOOGLE }); -}; - -export const getGithubUserData = async ({ - redirectUrl, - code, -}: { - redirectUrl: string; - code: string; -}) => { - const { GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET } = Bun.env; - if (!GITHUB_CLIENT_ID || !GITHUB_CLIENT_SECRET) { - throw new HTTPException(400, { - message: "Missing Github access env", - }); - } - const params = new URLSearchParams(); - params.append("grant_type", "authorization_code"); - params.append("code", code); - params.append("redirect_uri", redirectUrl); - params.append("client_id", GITHUB_CLIENT_ID); - params.append("client_secret", GITHUB_CLIENT_SECRET); - const githubTokenOptions = { - method: "POST", - data: params.toString(), - url: Bun.env.GITHUB_TOKEN_ENDPOINT, - }; - const githubTokenResponse = await axios(githubTokenOptions); - if (githubTokenResponse.status !== 200) { - throw new HTTPException(400, { - message: "Error while getting Github response", - }); - } - const access_token = githubTokenResponse.data.split("&")[0].split("=")[1]; - const githubUserInfoOptions = { - method: "GET", - headers: { Authorization: `Bearer ${access_token}` }, - url: process.env.GITHUB_USERINFO_ENDPOINT, - }; - const githubUserResponse = await axios(githubUserInfoOptions); - if (githubUserResponse.status !== 200) { - throw new HTTPException(400, { - message: "Error while getting Github response", - }); - } - return githubUserResponse.data[0]; -}; diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 9eb4d27..867e720 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -15,14 +15,17 @@ import { EnvironmentRepository } from "../repositories/mongodb"; const updateUserTelegramId = async ({ telegramId, context, - email, + clerkUserId, }: { telegramId?: number; - email: string; + clerkUserId: string; context: Context; }): Promise => { const repository = context.get(USER_MONGO_DB_REPOSITORY); - const user = await repository.updateUserTelegramId({ email, telegramId }); + const user = await repository.updateUserTelegramId({ + clerkUserId, + telegramId, + }); if (!user) { throw new ServiceError(httpError.USER_NOT_FOUND); } @@ -41,6 +44,17 @@ const findUserByEmail = async ({ return repository.findUserByEmail(email); }; +const findUserByClerkId = async ({ + id, + context, +}: { + id: string; + context: Context; +}): Promise => { + const repository = context.get(USER_MONGO_DB_REPOSITORY); + return repository.findUserClerkId(id); +}; + const createOrFetchUser = async ({ user, context, @@ -54,7 +68,7 @@ const createOrFetchUser = async ({ throw new ServiceError(httpError.USER_DOES_NOT_HAVE_EMAIL); } - const dbUser = await findUserByEmail({ email: userEmail, context }); + const dbUser = await findUserByClerkId({ id: user.id, context }); if (dbUser) { return dbUser; @@ -70,12 +84,16 @@ const createOrFetchUser = async ({ envName: defaultNodbEnv, }); - // TODO remove provider return repository.createUser({ - provider: "github", + clerkUserId: user.id, email: userEmail, appName, }); }; -export { updateUserTelegramId, createOrFetchUser, findUserByEmail }; +export { + updateUserTelegramId, + createOrFetchUser, + findUserByEmail, + findUserByClerkId, +}; diff --git a/src/utils/auth-utils.ts b/src/utils/auth-utils.ts index 4a2e942..a72ef5c 100644 --- a/src/utils/auth-utils.ts +++ b/src/utils/auth-utils.ts @@ -1,10 +1,8 @@ import crypto from "crypto"; +import { type User } from "../models/user.model.ts"; -export type USER_TYPE = { - email: string; - applications: []; - lastProvider: string; -}; +// TODO remove this type and use User everywhere +export type USER_TYPE = User; export const generateState = (length = 32) => { const randomBytes = crypto.randomBytes(length); From f35e610ed3dca64ccb32bfa9436658e67b4b3b22 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Thu, 16 May 2024 15:43:29 +0200 Subject: [PATCH 12/17] add missing await so the middleware sets the user correctly --- src/middlewares/auth.middleware.ts | 3 +-- src/routes/users.ts | 27 ++++++--------------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index 4a3f2af..438f7c6 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -7,7 +7,6 @@ import { findUserByClerkId } from "../services/user.service.ts"; const factory = createFactory(); const middleware = factory.createMiddleware(async (c, next) => { - const clerkClient = c.get("clerk"); const auth = getAuth(c); if (!auth?.userId) { @@ -16,7 +15,7 @@ const middleware = factory.createMiddleware(async (c, next) => { }); } - const user = findUserByClerkId({ + const user = await findUserByClerkId({ id: auth.userId, context: c.get("context"), }); diff --git a/src/routes/users.ts b/src/routes/users.ts index 1e7f4d2..51dd162 100644 --- a/src/routes/users.ts +++ b/src/routes/users.ts @@ -4,17 +4,16 @@ import type Context from "../middlewares/context.ts"; import { type ClerkClient } from "@clerk/backend"; import { HTTPException } from "hono/http-exception"; import { httpError } from "../utils/const.ts"; -import { - createOrFetchUser, - findUserByEmail, -} from "../services/user.service.ts"; +import { createOrFetchUser } from "../services/user.service.ts"; import telegramRoute from "./users/telegram.ts"; import authMiddleware from "../middlewares/auth.middleware.ts"; +import { type User } from "../models/user.model.ts"; const app = new Hono<{ Variables: { context: Context; clerk: ClerkClient; + user: User; }; }>(); @@ -49,26 +48,12 @@ app.post("/auth", async (c) => { } }); -app.get("/profile", async (c) => { - const clerkClient = c.get("clerk"); - const auth = getAuth(c); - - if (!auth?.userId) { - return c.json({ - message: "You are not logged in.", - }); - } - const clerkUser = await clerkClient.users.getUser(auth.userId); - - const user = await findUserByEmail({ - email: clerkUser.primaryEmailAddress?.emailAddress || "", - context: c.get("context"), - }); +app.use(authMiddleware); - return c.json(user); +app.get("/profile", async (c) => { + return c.json(c.get("user")); }); -app.use(authMiddleware); app.route("/settings/telegram", telegramRoute); export default app; From 629a15aa9d0eb45615b243b14d4d69b964828515 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Fri, 17 May 2024 08:30:41 +0200 Subject: [PATCH 13/17] update auth middleware --- src/middlewares/auth.middleware.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index 438f7c6..f739be4 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -14,18 +14,29 @@ const middleware = factory.createMiddleware(async (c, next) => { message: httpError.USER_NOT_AUTHENTICATED, }); } + try { + const user = await findUserByClerkId({ + id: auth.userId, + context: c.get("context"), + }); - const user = await findUserByClerkId({ - id: auth.userId, - context: c.get("context"), - }); + if (!user) { + throw new HTTPException(401, { + message: httpError.USER_NOT_AUTHENTICATED, + }); + } - if (!user) { - throw new HTTPException(404, { message: httpError.USER_NOT_FOUND }); + c.set("user", user); + await next(); + } catch (e) { + if (e instanceof HTTPException) { + throw e; + } else { + throw new HTTPException(500, { + message: httpError.UNKNOWN, + }); + } } - - c.set("user", user); - await next(); }); export default middleware; From e50cd547bf903624877cbf408f4aa15571f3b46a Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Fri, 24 May 2024 15:52:19 +0200 Subject: [PATCH 14/17] wip with setting up complete flow for telegram bot --- bun.lockb | Bin 60271 -> 60743 bytes package.json | 3 +- src/app.ts | 2 +- src/models/user.model.ts | 19 ++++++++- src/repositories/interfaces.ts | 1 + src/repositories/mongodb/user.repository.ts | 18 +++++++- src/routes/webhooks/telegram.ts | 30 ++++++++++--- src/services/telegram.service.ts | 45 ++++++++++++++++---- src/services/user.service.ts | 12 ++++++ 9 files changed, 112 insertions(+), 18 deletions(-) diff --git a/bun.lockb b/bun.lockb index d7740af34dbd6588b81a29f45a583f91c4885133..d6c83c76dfb8fbbabb67144a2469877b7870c376 100755 GIT binary patch delta 8288 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 g&Z#8&KMFFsZq7?O*4*>K>RCOP&eRRG#nc&t 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 findUserByEmail(email: string): Promise; findUserClerkId(id: string): Promise; + findUserByTelegramId(id: number): Promise; } diff --git a/src/repositories/mongodb/user.repository.ts b/src/repositories/mongodb/user.repository.ts index ca13b34..3978f49 100644 --- a/src/repositories/mongodb/user.repository.ts +++ b/src/repositories/mongodb/user.repository.ts @@ -1,6 +1,7 @@ import BaseRepository from "./base-repository.ts"; import { type User } from "../../models/user.model.ts"; import type { IUserRepository } from "../interfaces.ts"; +import * as R from "ramda"; class UserRepository extends BaseRepository implements IUserRepository { constructor() { @@ -16,11 +17,14 @@ class UserRepository extends BaseRepository implements IUserRepository { email: string; appName: string; }): Promise { - return this.userModel.create({ + const result = await this.userModel.create({ email, clerkUserId, applications: [appName], }); + + // convert null values to undefined + return R.defaultTo(undefined, result) as User; } public async updateUserLastUse({ @@ -44,9 +48,15 @@ class UserRepository extends BaseRepository implements IUserRepository { clerkUserId: string; telegramId?: number; }): Promise { + const updateObj = telegramId + ? { "telegram.id": telegramId } + : { telegram: undefined }; + return this.userModel.findOneAndUpdate( { clerkUserId }, - { telegramId }, + { + ...updateObj, + }, { returnNewDocument: true }, ); } @@ -58,6 +68,10 @@ class UserRepository extends BaseRepository implements IUserRepository { public async findUserClerkId(id: string): Promise { return this.userModel.findOne({ clerkUserId: id }); } + + public async findUserByTelegramId(id: number): Promise { + return this.userModel.findOne({ "telegram.id": id }); + } } export default UserRepository; diff --git a/src/routes/webhooks/telegram.ts b/src/routes/webhooks/telegram.ts index 01e092c..b8538be 100644 --- a/src/routes/webhooks/telegram.ts +++ b/src/routes/webhooks/telegram.ts @@ -3,8 +3,18 @@ import { handleWebhookMessage, type TelegramRequest, } from "../../services/telegram.service.ts"; +import type { USER_TYPE } from "../../utils/auth-utils.ts"; +import type Context from "../../middlewares/context.ts"; +import { ServiceError } from "../../utils/service-errors.ts"; +import { HTTPException } from "hono/http-exception"; +import { httpError } from "../../utils/const.ts"; -const app = new Hono(); +const app = new Hono<{ + Variables: { + user: USER_TYPE; + context: Context; + }; +}>(); // validation endpoint which is needed app.get("/", async (c) => { @@ -15,10 +25,20 @@ app.get("/", async (c) => { app.post("/", async (c) => { const body = (await c.req.json()) as TelegramRequest; - - await handleWebhookMessage(body); - - return c.json({}); + try { + await handleWebhookMessage({ + telegramRequestBody: body, + context: c.get("context"), + user: c.get("user"), + }); + return c.json({}); + } catch (error) { + if (error instanceof ServiceError) { + throw new HTTPException(400, { message: error.explicitMessage }); + } else { + throw new HTTPException(500, { message: httpError.UNKNOWN }); + } + } }); export default app; diff --git a/src/services/telegram.service.ts b/src/services/telegram.service.ts index 71cce8e..2c36a2c 100644 --- a/src/services/telegram.service.ts +++ b/src/services/telegram.service.ts @@ -1,3 +1,10 @@ +import { searchAiEntities } from "./entity.service.ts"; +import type Context from "../middlewares/context.ts"; +import { type User } from "../models/user.model.ts"; +import { httpError } from "../utils/const.ts"; +import { ServiceError } from "../utils/service-errors.ts"; +import { findUserByTelegramId } from "./user.service.ts"; + const telegramBaseUrl = `https://api.telegram.org/bot${Bun.env.TELEGRAM_TOKEN}`; interface TelegramRequest { @@ -24,27 +31,49 @@ interface TelegramMessage { text: string; } -const handleWebhookMessage = async ( - telegramRequestBody: TelegramRequest, -): Promise => { +const handleWebhookMessage = async ({ + telegramRequestBody, + context, +}: { + telegramRequestBody: TelegramRequest; + context: Context; + user: User; +}): Promise => { const { message } = telegramRequestBody; if (!Bun.env.TELEGRAM_TOKEN) { return; } - const result = await fetch(`${telegramBaseUrl}/sendMessage`, { + + const userTelegramId = message.from.id; + const user = await findUserByTelegramId({ id: userTelegramId, context }); + + if (!user || !user.telegram) { + throw new ServiceError(httpError.USER_NOT_FOUND); + } + + const appFilter = `${user.telegram.appName}/${user.telegram.envName}`; + + const res = await searchAiEntities({ + context, + query: message.text, + entityType: appFilter, + }); + + if (!res) { + throw new ServiceError(httpError.UNKNOWN); + } + + await fetch(`${telegramBaseUrl}/sendMessage`, { method: "POST", headers: { "Content-type": "application/json", }, body: JSON.stringify({ chat_id: message.chat.id, - text: "Hello from Hono app", + text: res.answer as string, }), }); - - console.log(result.status); - console.log(await result.json()); }; export { handleWebhookMessage }; diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 867e720..4365ca2 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -55,6 +55,17 @@ const findUserByClerkId = async ({ return repository.findUserClerkId(id); }; +const findUserByTelegramId = async ({ + id, + context, +}: { + id: number; + context: Context; +}): Promise => { + const repository = context.get(USER_MONGO_DB_REPOSITORY); + return repository.findUserByTelegramId(id); +}; + const createOrFetchUser = async ({ user, context, @@ -96,4 +107,5 @@ export { createOrFetchUser, findUserByEmail, findUserByClerkId, + findUserByTelegramId, }; From 4e4f75612fe9abacfe8b6606d9101b6e175b5c23 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Mon, 27 May 2024 08:38:33 +0200 Subject: [PATCH 15/17] extend model with adding whatsapp settings to the model as well --- src/models/user.model.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/models/user.model.ts b/src/models/user.model.ts index ae99049..2201ce4 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -5,7 +5,16 @@ const { Schema } = mongoose; export const TelegramSchema = new Schema( { - id: { type: Number, required: true, index: true }, + id: { type: Number, required: true, index: true, unique: true }, + appName: { type: String, required: true }, + envName: { type: String, required: true }, + }, + { _id: false }, +); + +export const WhatsappSchema = new Schema( + { + id: { type: String, required: true, index: true, unique: true }, appName: { type: String, required: true }, envName: { type: String, required: true }, }, @@ -26,6 +35,10 @@ export const UserSchema = new Schema({ type: TelegramSchema, required: false, }, + whatsapp: { + type: WhatsappSchema, + required: false, + }, }); export type User = { From 5c59822c0918d9fe21aefa6d199d21b2d64e2510 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Mon, 27 May 2024 09:02:31 +0200 Subject: [PATCH 16/17] update user telegram settings --- src/repositories/interfaces.ts | 6 +++--- src/repositories/mongodb/user.repository.ts | 15 ++++++++++----- src/routes/users/telegram.ts | 9 +++++---- src/services/user.service.ts | 18 ++++++++++++------ src/utils/const.ts | 2 ++ src/utils/types.ts | 6 ++++++ 6 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/repositories/interfaces.ts b/src/repositories/interfaces.ts index c5229fe..2db7992 100644 --- a/src/repositories/interfaces.ts +++ b/src/repositories/interfaces.ts @@ -2,7 +2,7 @@ import { type Application } from "../models/application.model.ts"; import { type Environment } from "../models/environment.model.ts"; import { type Entity } from "../models/entity.model.ts"; import { type User } from "../models/user.model.ts"; -import type { EntityQueryMeta } from "../utils/types.ts"; +import type { EntityQueryMeta, TelegramSettings } from "../utils/types.ts"; import type { EntityAggregateResult } from "../services/entity.service.ts"; export interface IApplicationRepository { @@ -120,9 +120,9 @@ export interface IUserRepository { appName: string; }): Promise; updateUserLastUse(props: { clerkUserId: string }): Promise; - updateUserTelegramId(props: { + updateUserTelegramSettings(props: { clerkUserId: string; - telegramId?: number; + telegramSettings?: TelegramSettings; }): Promise; findUserByEmail(email: string): Promise; findUserClerkId(id: string): Promise; diff --git a/src/repositories/mongodb/user.repository.ts b/src/repositories/mongodb/user.repository.ts index 3978f49..4072b58 100644 --- a/src/repositories/mongodb/user.repository.ts +++ b/src/repositories/mongodb/user.repository.ts @@ -2,6 +2,7 @@ import BaseRepository from "./base-repository.ts"; import { type User } from "../../models/user.model.ts"; import type { IUserRepository } from "../interfaces.ts"; import * as R from "ramda"; +import type { TelegramSettings } from "../../utils/types.ts"; class UserRepository extends BaseRepository implements IUserRepository { constructor() { @@ -41,15 +42,19 @@ class UserRepository extends BaseRepository implements IUserRepository { ); } - public async updateUserTelegramId({ + public async updateUserTelegramSettings({ clerkUserId, - telegramId, + telegramSettings, }: { clerkUserId: string; - telegramId?: number; + telegramSettings?: TelegramSettings; }): Promise { - const updateObj = telegramId - ? { "telegram.id": telegramId } + const updateObj = telegramSettings?.telegramId + ? { + "telegram.id": telegramSettings.telegramId, + "telegram.appName": telegramSettings.appName, + "telegram.envName": telegramSettings.envName, + } : { telegram: undefined }; return this.userModel.findOneAndUpdate( diff --git a/src/routes/users/telegram.ts b/src/routes/users/telegram.ts index e5b4f07..893be05 100644 --- a/src/routes/users/telegram.ts +++ b/src/routes/users/telegram.ts @@ -2,8 +2,9 @@ import { Hono } from "hono"; import { HTTPException } from "hono/http-exception"; import type Context from "../../middlewares/context.ts"; import type { USER_TYPE } from "../../utils/auth-utils.ts"; -import { updateUserTelegramId } from "../../services/user.service.ts"; +import { updateUserTelegramSettings } from "../../services/user.service.ts"; import { ServiceError } from "../../utils/service-errors.ts"; +import type { TelegramSettings } from "../../utils/types.ts"; const app = new Hono<{ Variables: { @@ -15,10 +16,10 @@ const app = new Hono<{ // TODO cover with e2e tests app.patch("/", async (c) => { try { - const body = await c.req.json(); + const body = (await c.req.json()) as TelegramSettings; - await updateUserTelegramId({ - telegramId: body.telegramId, + await updateUserTelegramSettings({ + telegramSettings: body, clerkUserId: c.get("user").clerkUserId, context: c.get("context"), }); diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 4365ca2..baf5070 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -11,20 +11,26 @@ import { ServiceError } from "../utils/service-errors.ts"; import { type User as ClerkUser } from "@clerk/backend"; import generateAppName from "../utils/app-name.ts"; import { EnvironmentRepository } from "../repositories/mongodb"; +import type { TelegramSettings } from "../utils/types.ts"; +import * as R from "ramda"; -const updateUserTelegramId = async ({ - telegramId, +const updateUserTelegramSettings = async ({ + telegramSettings, context, clerkUserId, }: { - telegramId?: number; + telegramSettings: TelegramSettings; clerkUserId: string; context: Context; }): Promise => { + if (R.keys(R.filter(R.isNil, telegramSettings)).length === 0) { + throw new ServiceError(httpError.USER_TELEGRAM_SETTINGS_MISSING); + } + const repository = context.get(USER_MONGO_DB_REPOSITORY); - const user = await repository.updateUserTelegramId({ + const user = await repository.updateUserTelegramSettings({ clerkUserId, - telegramId, + telegramSettings, }); if (!user) { throw new ServiceError(httpError.USER_NOT_FOUND); @@ -103,7 +109,7 @@ const createOrFetchUser = async ({ }; export { - updateUserTelegramId, + updateUserTelegramSettings, createOrFetchUser, findUserByEmail, findUserByClerkId, diff --git a/src/utils/const.ts b/src/utils/const.ts index ec5d57b..e86d7f7 100644 --- a/src/utils/const.ts +++ b/src/utils/const.ts @@ -62,6 +62,8 @@ export const httpError = { USER_NOT_FOUND: "Can't find user", USER_NOT_AUTHENTICATED: "Unauthenticated", USER_DOES_NOT_HAVE_EMAIL: "User does not have a linked email", + USER_TELEGRAM_SETTINGS_MISSING: + "User does not have all required settings for setting up telegram", ENV_CANT_CREATE: "Couldn't create environment", APPNAME_LENGTH: `Application name length must be greater than ${APPNAME_MIN_LENGTH}`, APPNAME_NOT_ALLOWED: diff --git a/src/utils/types.ts b/src/utils/types.ts index b4bff72..3ef1c26 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -43,3 +43,9 @@ export type Pagination = { previous_page?: string; next_page?: string; }; + +export type TelegramSettings = { + telegramId: number; + appName: string; + envName: string; +}; From 8a9de9715337256091a319ad5402c07dd0140119 Mon Sep 17 00:00:00 2001 From: Kristian Tot Date: Tue, 4 Jun 2024 14:30:43 +0200 Subject: [PATCH 17/17] update packages --- bun.lockb | Bin 60743 -> 61716 bytes package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/bun.lockb b/bun.lockb index d6c83c76dfb8fbbabb67144a2469877b7870c376..5bf3fb30fe817e24411c4a576dd78652ee222915 100755 GIT binary patch delta 1703 zcmc(f?^Bdj6vvA0{o?5p*WzeC-`;b3kI@GE36ykLs%GXQZh*UX-To91>jOC{36v z9kSQw+7%uvFybi)1qOdX@F2B&uucjQgsB+ug6ApdHrSKd4jt1fjDrG#t%UA$X1LcX zrtEIXYi(Y2>QH`s|5au2N#_hzv_HM-8KpcnUl0~zo4dN)pg_)%IVA>OAly(Nw zk6}jfG%2+v0AB42?BFM%gOKX?+{1T3sYa0^J_4q*AOW;4|M??X?>baa4oufFvE3Swl+2uI05#FXh5_x70ES4Z@1dh6)Z=e`^&YB_7^|HA(8 z&Bb?KI@QrSJ<&_q+Wnv=u5ZrWT^HA`x$T(0O?{RM-#xbcAX$cD1L}Jy$>JR^|6aWD z_~xyb@*MiL9>PnT|%@=o`lY>QbuK+UjzfaRZV77tQJdIIHUc+h!R6*Mm+ zfdZ`_RFq*Bk5D_TE?ALPvsgui)&$W`7tmHab(xnZQ;8~&np6r^x-~T`D=pO;6YTWK z-!Yd8a#B>ic2`COTQzALy{NG=B!6UdOxSS{aL%|&SlH$hW+qO*0)3w22Ce~?!+^)Q z+6Dm25x`^a05$KDO0+!2)p*Y}0$0`e;3Vj!fEU;SZ_0rf0E+|g;-+f01R=hLVjX^T zyee@K$HrC5m3$j;rE_f8!42>y;J60CU!WUY1U=vnT4B$O=4iOi!@zUkS>OkJ0N zpch;LE#MSr1;5fm`+oQD(d(dn)f?PTqj!$(R|n`j(dz|;df@&E)g>@H|euOV*-QFHaNAlF>1Pc zLPJM^?S$8BdRd3LOZ~?lP-D}in(Z~Kvn4T7j*uhZ$;`33zSy%hBTsI(2jNS4Subnh z$=Vp2y5&7#qwl)<^j$Y@!4obUWG#80oLzQO6JbqVzaa~2OKx;snpx6n^AZQ~h;mdd z$SOa&+q@S(N-JLV_P-w5{OzD5g-~LHUTmYi4Krs=JlN02?Jp4e$~v)2w7h`#C;F}- zTkq2VbX#9?r#DBmq`OR7GoqNxmJGWt6bAL!v zYhGV4+nv?vjsEtm_WI=8&9VANv4NP&kvHE|4fYN1I~=*|#4=O{FBuNjOqaM>;)T%avZ7qG-V3Ti~$Yin0 zUrlt-Viy_IWpU_FV9iv_)lhd>ntUsPOp09yDpDL`J9Q}zeLWVpV}hPaBGTkQBAKjq zv5<c$A zq9)qp%A+Yn=UqbnSkDT> zR13Zms&1tYPd1HvmhPJCMH(T+5vb@yN7Dv9ez0q6@9~>X%TQCH;nAc#K#=POiUU;{G5BMq9 z>(YII_m29#%c38{>jzK3QxE`mKp(hEqP<2p0Ph~T+Y5Aq@b1&k_H`!y0oH*Sz-9A~ zuoY|rHc;rr16pzcWv+?lqWOEdU#EZ@GynoeRd}i8`r%6!Ikdr_Y$)cA sLRWj~kUyKQ`YlmaxYP@<|8ZN&V^I<{9 diff --git a/package.json b/package.json index 721e522..20c366a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@anthropic-ai/sdk": "^0.20.6", - "@clerk/backend": "^1.1.5", + "@clerk/backend": "^1.2.1", "@clerk/clerk-sdk-node": "^5.0.7", "@hono/clerk-auth": "^2.0.0", "@langchain/openai": "^0.0.28",