Skip to content

Commit

Permalink
add users
Browse files Browse the repository at this point in the history
  • Loading branch information
vorant94 committed Nov 13, 2024
1 parent abae16a commit 9ec9247
Show file tree
Hide file tree
Showing 18 changed files with 347 additions and 18 deletions.
2 changes: 2 additions & 0 deletions drizzle.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { config } from "dotenv";
import { defineConfig } from "drizzle-kit";
import { envSchema } from "./src/shared/context/env.ts";
import { dbConfig } from "./src/shared/schema/db-config";

const env = envSchema.parse(config().parsed);

export default defineConfig({
...dbConfig,
out: "./drizzle",
schema: "./src/shared/schema",
dialect: "sqlite",
Expand Down
16 changes: 16 additions & 0 deletions drizzle/0001_bored_lord_tyger.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
ALTER TABLE `chains` RENAME COLUMN "createdAt" TO "created_at";--> statement-breakpoint
ALTER TABLE `chains` RENAME COLUMN "updatedAt" TO "updated_at";--> statement-breakpoint
CREATE TABLE `users` (
`id` text PRIMARY KEY NOT NULL,
`resource_type` text DEFAULT 'user' NOT NULL,
`created_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
`created_by` text,
`updated_at` text DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
`telegram_chat_id` integer NOT NULL,
FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_telegramChatId_unique` ON `users` (`telegram_chat_id`);--> statement-breakpoint
ALTER TABLE `chains` ADD `resource_type` text DEFAULT 'chain' NOT NULL;--> statement-breakpoint
ALTER TABLE `chains` ADD `created_by` text NOT NULL REFERENCES users(id);--> statement-breakpoint
ALTER TABLE `chains` ADD `name` text NOT NULL;
157 changes: 157 additions & 0 deletions drizzle/meta/0001_snapshot.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
{
"version": "6",
"dialect": "sqlite",
"id": "2b4f22d3-5a7a-44b0-85f8-0c48a86ba4e3",
"prevId": "73990216-a750-4ce5-b29d-8a573b6ceae1",
"tables": {
"chains": {
"name": "chains",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"resource_type": {
"name": "resource_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'chain'"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"created_by": {
"name": "created_by",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"chains_created_by_users_id_fk": {
"name": "chains_created_by_users_id_fk",
"tableFrom": "chains",
"tableTo": "users",
"columnsFrom": ["created_by"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"users": {
"name": "users",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"resource_type": {
"name": "resource_type",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "'user'"
},
"created_at": {
"name": "created_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"created_by": {
"name": "created_by",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"updated_at": {
"name": "updated_at",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "(CURRENT_TIMESTAMP)"
},
"telegram_chat_id": {
"name": "telegram_chat_id",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"users_telegramChatId_unique": {
"name": "users_telegramChatId_unique",
"columns": ["telegram_chat_id"],
"isUnique": true
}
},
"foreignKeys": {
"users_created_by_users_id_fk": {
"name": "users_created_by_users_id_fk",
"tableFrom": "users",
"tableTo": "users",
"columnsFrom": ["created_by"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {
"\"chains\".\"createdAt\"": "\"chains\".\"created_at\"",
"\"chains\".\"updatedAt\"": "\"chains\".\"updated_at\""
}
},
"internal": {
"indexes": {}
}
}
7 changes: 7 additions & 0 deletions drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"when": 1731436645896,
"tag": "0000_spotty_winter_soldier",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1731512172017,
"tag": "0001_bored_lord_tyger",
"breakpoints": true
}
]
}
4 changes: 2 additions & 2 deletions src/api/api/chains.route.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Hono } from "hono";
import { listChains } from "../../bl/chains/chains.bl.ts";
import { findChains } from "../../bl/chains/chains.bl.ts";

export const chainsRoute = new Hono();

chainsRoute.get("/", async (hc) => hc.json(await listChains()));
chainsRoute.get("/", async (hc) => hc.json(await findChains()));
19 changes: 19 additions & 0 deletions src/api/telegram/auth.composer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Composer } from "grammy";
import { getContext } from "hono/context-storage";
import { findOrCreateUserByTelegramChatId } from "../../bl/users/users.bl.ts";
import type { Context } from "../../shared/context/context.ts";

export const authComposer = new Composer();

authComposer.use(async (tc, next) => {
const hc = getContext<Context>();

if (!tc.chat) {
throw new Error("Expect chat to be defined!");
}

const user = await findOrCreateUserByTelegramChatId(tc.chat.id);
hc.set("user", user);

await next();
});
8 changes: 7 additions & 1 deletion src/api/telegram/echo.composer.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { Composer } from "grammy";
import { getContext } from "hono/context-storage";
import type { Context } from "../../shared/context/context.ts";

export const echoComposer = new Composer();

echoComposer.on("message", (tc) => {
return tc.reply(tc.message.text ?? "no text in your message");
const { user } = getContext<Context>().var;

return tc.reply(
`you are ${user.id} and you wrote ${tc.message.text ? `"${tc.message.text}"` : "nothing"}`,
);
});
3 changes: 3 additions & 0 deletions src/api/telegram/telegram.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@ import { webhookCallback } from "grammy";
import { Hono } from "hono";
import { getContext } from "hono/context-storage";
import type { Context } from "../../shared/context/context.ts";
import { authComposer } from "./auth.composer.ts";
import { echoComposer } from "./echo.composer.ts";

export const telegramRoute = new Hono();

telegramRoute.use("/", (hc) => {
const { bot } = getContext<Context>().var;

bot.use(authComposer);

bot.use(echoComposer);

return webhookCallback(bot, "cloudflare-mod")(hc.req.raw);
Expand Down
6 changes: 3 additions & 3 deletions src/bl/chains/chains.bl.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { selectChains } from "../../dal/chains/chains.table.ts";
import { findAllChains } from "../../dal/chains/chains.table.ts";
import type { Chain } from "../../shared/schema/chains.ts";

export async function listChains(): Promise<Array<Chain>> {
return await selectChains();
export async function findChains(): Promise<Array<Chain>> {
return await findAllChains();
}
16 changes: 16 additions & 0 deletions src/bl/users/users.bl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import {
createUser,
findUserByTelegramChatId,
} from "../../dal/users/users.table.ts";
import type { User } from "../../shared/schema/users.ts";

export async function findOrCreateUserByTelegramChatId(
telegramChatId: User["telegramChatId"],
): Promise<User> {
const existing = await findUserByTelegramChatId(telegramChatId);
if (existing) {
return existing;
}

return await createUser({ telegramChatId });
}
10 changes: 3 additions & 7 deletions src/dal/chains/chains.table.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import { getContext } from "hono/context-storage";
import type { Context } from "../../shared/context/context.ts";
import {
type Chain,
chains,
selectChainSchema,
} from "../../shared/schema/chains.ts";
import { type Chain, chainSchema, chains } from "../../shared/schema/chains.ts";

export async function selectChains(): Promise<Array<Chain>> {
export async function findAllChains(): Promise<Array<Chain>> {
const { db } = getContext<Context>().var;

return (await db.select().from(chains)).map((chain) =>
selectChainSchema.parse(chain),
chainSchema.parse(chain),
);
}
36 changes: 36 additions & 0 deletions src/dal/users/users.table.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { sql } from "drizzle-orm";
import { getContext } from "hono/context-storage";
import type { Context } from "../../shared/context/context.ts";
import {
type InsertUser,
type User,
createUserSchema,
userSchema,
users,
} from "../../shared/schema/users.ts";

export async function createUser(raw: InsertUser): Promise<User> {
const { db, user } = getContext<Context>().var;

const toCreate = createUserSchema.parse(raw);

return userSchema.parse(
await db
.insert(users)
.values({ ...toCreate, createdBy: user?.id })
.returning(),
);
}

export async function findUserByTelegramChatId(
telegramChatId: User["telegramChatId"],
): Promise<User | null> {
const { db } = getContext<Context>().var;

const [raw] = await db
.select()
.from(users)
.where(sql`${users.telegramChatId} = ${telegramChatId}`);

return raw ? userSchema.parse(raw) : null;
}
5 changes: 3 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { healthRoute } from "./api/health/health.route.ts";
import { telegramRoute } from "./api/telegram/telegram.route.ts";
import type { Context } from "./shared/context/context.ts";
import { envSchema } from "./shared/context/env.ts";
import { dbConfig } from "./shared/schema/db-config.ts";

if (import.meta.env.DEV) {
// dotenv needed during development to set env locally from process.env
Expand All @@ -27,11 +28,11 @@ app.use(contextStorage(), async (hc, next) => {

if (import.meta.env.DEV) {
const { drizzle } = await import("drizzle-orm/libsql");
const db = drizzle(parsedEnv.DB_FILE_NAME);
const db = drizzle(parsedEnv.DB_FILE_NAME, dbConfig);
hc.set("db", db);
} else {
const { drizzle } = await import("drizzle-orm/d1");
const db = drizzle(hc.env.DB);
const db = drizzle(hc.env.DB, dbConfig);
hc.set("db", db);
}

Expand Down
2 changes: 2 additions & 0 deletions src/shared/context/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { DrizzleD1Database } from "drizzle-orm/d1";
import type { LibSQLDatabase } from "drizzle-orm/libsql";
import type { Bot } from "grammy";
import type { Env as HonoEnv } from "hono";
import type { User } from "../schema/users.ts";
import type { Env } from "./env.ts";

export interface Context extends HonoEnv {
Expand All @@ -10,6 +11,7 @@ export interface Context extends HonoEnv {
env: Env;
bot: Bot;
db: DrizzleD1Database | LibSQLDatabase;
user: User;
};
// biome-ignore lint/style/useNamingConvention: 3-rd party type
Bindings: {
Expand Down
Loading

0 comments on commit 9ec9247

Please sign in to comment.