From 40bfb085c62894ce032896cef2fc44cabab8656f Mon Sep 17 00:00:00 2001 From: Fergal Moran Date: Mon, 29 Jan 2024 19:25:27 +0000 Subject: [PATCH] Add pings and seeds --- .vscode/settings.json | 3 +- bun.lockb | Bin 271397 -> 271752 bytes package.json | 4 +- scripts/reset.sh | 18 ++++ src/components/children/child-select-list.tsx | 21 ++--- src/components/children/children-filter.tsx | 14 +-- src/components/maps/main-map.tsx | 20 ++++- src/components/pages/dashboard-page.tsx | 2 +- src/lib/api/children/queries.ts | 34 +++++--- src/lib/auth/utils.ts | 10 +-- src/server/api/routers/children.ts | 1 - src/server/db/index.ts | 6 ++ src/server/db/migrate.ts | 13 ++- src/server/db/schema/_root.ts | 22 +++-- src/server/db/schema/children.ts | 5 ++ src/server/db/schema/devices.ts | 55 ++++++++---- src/server/db/schema/pings.ts | 15 ++-- src/server/db/seed/auth.ts | 31 +++++++ src/server/db/seed/seed.ts | 80 ++++++++++++++++++ 19 files changed, 276 insertions(+), 78 deletions(-) create mode 100755 scripts/reset.sh create mode 100644 src/server/db/seed/auth.ts create mode 100644 src/server/db/seed/seed.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 0b7dd4e..316fb18 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,6 @@ ".vscode": true, ".next": true, "node_modules": true - } + }, + "workbench.colorTheme": "poimandres-noitalics" } diff --git a/bun.lockb b/bun.lockb index 6287e4b203f079af2616ceb070e4c73ecc650f4e..a5fd113c22aa75ccce95b323e63a99c98f5fbc38 100755 GIT binary patch delta 45684 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 z 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 C2T#fX delta 45359 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

de2 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 diff --git a/package.json b/package.json index 2763d4e..7c76479 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "start": "next start", "db:generate": "drizzle-kit generate:pg", "db:migrate": "tsx src/server/db/migrate.ts", + "db:seed": "tsx src/server/db/seed/seed.ts && tsx src/server/db/seed/auth.ts", "db:drop": "drizzle-kit drop", "db:pull": "drizzle-kit introspect:pg", "db:studio": "drizzle-kit studio", @@ -19,6 +20,7 @@ "dependencies": { "@auth/core": "^0.22.0", "@auth/drizzle-adapter": "^0.3.16", + "@faker-js/faker": "^8.4.0", "@hookform/resolvers": "^3.3.4", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-alert-dialog": "^1.0.5", @@ -110,4 +112,4 @@ "ct3aMetadata": { "initVersion": "7.25.2" } -} \ No newline at end of file +} diff --git a/scripts/reset.sh b/scripts/reset.sh new file mode 100755 index 0000000..1050b78 --- /dev/null +++ b/scripts/reset.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +export PGUSER=postgres +export PGPASSWORD=hackme +export PGHOST=localhost + +echo Removing migrations +rm -rf drizzle +echo "Dropping db" + +dropdb -f --if-exists kidarr +echo "Creating db" +createdb kidarr + +bun db:generate +bun db:migrate + +# bun run src/db/migrate.ts +bun db:seed diff --git a/src/components/children/child-select-list.tsx b/src/components/children/child-select-list.tsx index f123c8c..00baa53 100644 --- a/src/components/children/child-select-list.tsx +++ b/src/components/children/child-select-list.tsx @@ -1,5 +1,5 @@ -'use client'; -import React from 'react'; +"use client"; +import React from "react"; import { Select, SelectContent, @@ -7,15 +7,15 @@ import { SelectItem, SelectTrigger, SelectValue, -} from '@/components/ui/select'; -import type ChildModel from '@/lib/models/child'; +} from "@/components/ui/select"; +import { type Child } from "@/server/db/schema/children"; type ChildSelectListProps = { - kids: ChildModel[]; -} -const ChildSelectList: React.FC = ({ kids }) => { + children: Child[]; +}; +const ChildSelectList: React.FC = ({ children: kids }) => { return ( - @@ -23,10 +23,7 @@ const ChildSelectList: React.FC = ({ kids }) => { (All Children) {kids?.map((r) => ( - + {r.name} ))} diff --git a/src/components/children/children-filter.tsx b/src/components/children/children-filter.tsx index 9e0bfc5..7489fd5 100644 --- a/src/components/children/children-filter.tsx +++ b/src/components/children/children-filter.tsx @@ -1,15 +1,19 @@ -import React from "react"; +"use client"; +import React, { useEffect } from "react"; import ChildSelectList from "./child-select-list"; -import { type CompleteChild } from "@/server/db/schema/children"; +import { type Child } from "@/server/db/schema/children"; type ChildrenFilterProps = { - kids: CompleteChild[]; + children: Child[]; }; -const ChildrenFilter: React.FC = async ({ kids }) => { +const ChildrenFilter: React.FC = ({ children }) => { + useEffect(() => { + console.log("ChildrenFilter: useEffect", children); + }, [children]); return (

- + {/* */}
); diff --git a/src/components/maps/main-map.tsx b/src/components/maps/main-map.tsx index 679b677..32837f7 100644 --- a/src/components/maps/main-map.tsx +++ b/src/components/maps/main-map.tsx @@ -27,7 +27,25 @@ const MainMap: React.FC = ({ kids }) => { zoom={10} scrollWheelZoom={true} > - + + <> + {kids?.map((kid) => + kid.devices?.map((device) => + device.pings.map((ping) => ( + + )), + ), + )} + + ); diff --git a/src/components/pages/dashboard-page.tsx b/src/components/pages/dashboard-page.tsx index 334d473..d93a900 100644 --- a/src/components/pages/dashboard-page.tsx +++ b/src/components/pages/dashboard-page.tsx @@ -11,7 +11,7 @@ const DashboardPage = async () => { return (
- +
diff --git a/src/lib/api/children/queries.ts b/src/lib/api/children/queries.ts index 270adc3..dc0c7ca 100644 --- a/src/lib/api/children/queries.ts +++ b/src/lib/api/children/queries.ts @@ -1,24 +1,36 @@ -import { db } from "@/server/db/index"; import { eq, and } from "drizzle-orm"; import { getUserAuth } from "@/lib/auth/utils"; +import { db } from "@/server/db"; import { type ChildId, childIdSchema, children, } from "@/server/db/schema/children"; -import { devices } from "@/server/db/schema/devices"; -import { pings } from "@/server/db/schema/pings"; export const getChildren = async () => { const { session } = await getUserAuth(); - const c = await db - .select() - .from(children) - .where(eq(children.userId, session?.user.id!)) - .innerJoin(devices, eq(devices.childId, children.id)) - .innerJoin(pings, eq(pings.deviceId, devices.id)) - .orderBy(children.name); - return { children: c.map((c) => ({ ...c, devices: [] })) }; + console.log("queries", "getChildren", session?.user.id!); + const c = await db.query.children.findMany({ + where: (children, { eq }) => eq(children.userId, session?.user.id!), + orderBy: (children) => children.name, + with: { + devices: { + with: { + pings: true, + }, + }, + }, + }); + console.log("queries", "gotChildren", c); + return { children: c }; + // const c = await db + // .select() + // .from(children) + // .where(eq(children.userId, session?.user.id!)) + // .innerJoin(devices, eq(devices.childId, children.id)) + // .innerJoin(pings, eq(pings.deviceId, devices.id)) + // .orderBy(children.name); + // return c; }; export const getChildById = async (id: ChildId) => { diff --git a/src/lib/auth/utils.ts b/src/lib/auth/utils.ts index 5eb205b..f8c57fe 100644 --- a/src/lib/auth/utils.ts +++ b/src/lib/auth/utils.ts @@ -2,7 +2,7 @@ import { db } from "@/server/db/index"; import { DrizzleAdapter } from "@auth/drizzle-adapter"; import { DefaultSession, getServerSession, NextAuthOptions } from "next-auth"; import { redirect } from "next/navigation"; -import { env } from "@/env.mjs" +import { env } from "@/env.mjs"; import GoogleProvider from "next-auth/providers/google"; declare module "next-auth" { @@ -32,14 +32,13 @@ export const authOptions: NextAuthOptions = { }, }, providers: [ - GoogleProvider({ + GoogleProvider({ clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, - }) + }), ], }; - export const getUserAuth = async () => { const session = await getServerSession(authOptions); return { session } as AuthSession; @@ -47,6 +46,5 @@ export const getUserAuth = async () => { export const checkAuth = async () => { const { session } = await getUserAuth(); - if (!session) redirect("/api/auth/signin"); + if (!session) redirect("/signin"); }; - diff --git a/src/server/api/routers/children.ts b/src/server/api/routers/children.ts index 26657c5..00dbd75 100644 --- a/src/server/api/routers/children.ts +++ b/src/server/api/routers/children.ts @@ -2,7 +2,6 @@ import { getChildById, getChildren } from "@/lib/api/children/queries"; import { publicProcedure, createTRPCRouter, - protectedProcedure, } from "@/server/api/trpc"; import { childIdSchema, diff --git a/src/server/db/index.ts b/src/server/db/index.ts index 3353ef6..d59843d 100644 --- a/src/server/db/index.ts +++ b/src/server/db/index.ts @@ -8,6 +8,9 @@ import { verificationTokens, children, devices, + childrenRelations, + deviceRelations, + pingRelations, } from "@/server/db/schema/_root"; export const client = postgres(env.DATABASE_URL); @@ -19,5 +22,8 @@ export const db = drizzle(client, { accounts, sessions, verificationTokens, + childrenRelations, + deviceRelations, + pingRelations, }, }); diff --git a/src/server/db/migrate.ts b/src/server/db/migrate.ts index e3142de..b9afa86 100644 --- a/src/server/db/migrate.ts +++ b/src/server/db/migrate.ts @@ -1,26 +1,23 @@ import { env } from "@/env.mjs"; - + import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; import postgres from "postgres"; - const runMigrate = async () => { if (!env.DATABASE_URL) { throw new Error("DATABASE_URL is not defined"); } - -const connection = postgres(env.DATABASE_URL, { max: 1 }); - -const db = drizzle(connection); + const connection = postgres(env.DATABASE_URL, { max: 1 }); + const db = drizzle(connection); console.log("⏳ Running migrations..."); const start = Date.now(); - await migrate(db, { migrationsFolder: 'src/server/db/migrations' }); + await migrate(db, { migrationsFolder: "src/server/db/migrations" }); const end = Date.now(); @@ -33,4 +30,4 @@ runMigrate().catch((err) => { console.error("❌ Migration failed"); console.error(err); process.exit(1); -}); \ No newline at end of file +}); diff --git a/src/server/db/schema/_root.ts b/src/server/db/schema/_root.ts index 4a18c9e..ac7d07b 100644 --- a/src/server/db/schema/_root.ts +++ b/src/server/db/schema/_root.ts @@ -1,6 +1,16 @@ -import { users, accounts, sessions, verificationTokens } from "./auth" -import { children } from "./children"; -import { devices } from "./devices"; -import { pings } from "./pings"; - -export { pings, devices, children, users, accounts, sessions, verificationTokens } \ No newline at end of file +import { users, accounts, sessions, verificationTokens } from "./auth"; +import { children, childrenRelations } from "./children"; +import { deviceRelations, devices } from "./devices"; +import { pingRelations, pings } from "./pings"; +export { + pings, + devices, + children, + users, + accounts, + sessions, + verificationTokens, + childrenRelations, + deviceRelations, + pingRelations, +}; diff --git a/src/server/db/schema/children.ts b/src/server/db/schema/children.ts index f29fad9..f63d608 100644 --- a/src/server/db/schema/children.ts +++ b/src/server/db/schema/children.ts @@ -6,6 +6,8 @@ import { users } from "@/server/db/schema/auth"; import { type getChildren } from "@/lib/api/children/queries"; import { randomUUID } from "crypto"; +import { relations } from "drizzle-orm"; +import { devices } from "./devices"; export const children = pgTable("children", { id: varchar("id", { length: 191 }) @@ -18,6 +20,9 @@ export const children = pgTable("children", { .references(() => users.id, { onDelete: "cascade" }) .notNull(), }); +export const childrenRelations = relations(children, ({ many }) => ({ + devices: many(devices), +})); // Schema for children - used to validate API requests export const insertChildSchema = createInsertSchema(children); diff --git a/src/server/db/schema/devices.ts b/src/server/db/schema/devices.ts index 9a61ead..3b07d6f 100644 --- a/src/server/db/schema/devices.ts +++ b/src/server/db/schema/devices.ts @@ -1,38 +1,56 @@ -import { varchar, pgTable } from "drizzle-orm/pg-core"; +import { + varchar, + pgTable, + PgColumn, + PgTableWithColumns, +} from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import { z } from "zod"; -import { children } from "./children" +import { children } from "./children"; import { users } from "@/server/db/schema/auth"; import { type getDevices } from "@/lib/api/devices/queries"; import { randomUUID } from "crypto"; +import { relations } from "drizzle-orm"; +import { pings } from "./pings"; - -export const devices = pgTable('devices', { - id: varchar("id", { length: 191 }).primaryKey().$defaultFn(() => randomUUID()), +export const devices = pgTable("devices", { + id: varchar("id", { length: 191 }) + .primaryKey() + .$defaultFn(() => randomUUID()), name: varchar("name", { length: 256 }).notNull(), deviceId: varchar("device_id", { length: 256 }).notNull(), - childId: varchar("child_id", { length: 256 }).references(() => children.id, { onDelete: "cascade" }).notNull(), - userId: varchar("user_id", { length: 256 }).references(() => users.id, { onDelete: "cascade" }).notNull(), + childId: varchar("child_id", { length: 256 }) + .references(() => children.id, { onDelete: "cascade" }) + .notNull(), + userId: varchar("user_id", { length: 256 }) + .references(() => users.id, { onDelete: "cascade" }) + .notNull(), }); - +export const deviceRelations = relations(devices, ({ one, many }) => ({ + child: one(children, { + fields: [devices.childId], + references: [children.id], + }), + pings: many(pings), +})); // Schema for devices - used to validate API requests export const insertDeviceSchema = createInsertSchema(devices); export const insertDeviceParams = createSelectSchema(devices, { - childId: z.coerce.string().min(1) -}).omit({ + childId: z.coerce.string().min(1), +}).omit({ id: true, - userId: true + userId: true, }); export const updateDeviceSchema = createSelectSchema(devices); -export const updateDeviceParams = createSelectSchema(devices,{ - childId: z.coerce.string().min(1) -}).omit({ - userId: true +export const updateDeviceParams = createSelectSchema(devices, { + childId: z.coerce.string().min(1), +}).omit({ + userId: true, }); export const deviceIdSchema = updateDeviceSchema.pick({ id: true }); @@ -43,7 +61,8 @@ export type NewDevice = z.infer; export type NewDeviceParams = z.infer; export type UpdateDeviceParams = z.infer; export type DeviceId = z.infer["id"]; - -// this type infers the return from getDevices() - meaning it will include any joins -export type CompleteDevice = Awaited>["devices"][number]; +// this type infers the return from getDevices() - meaning it will include any joins +export type CompleteDevice = Awaited< + ReturnType +>["devices"][number]; diff --git a/src/server/db/schema/pings.ts b/src/server/db/schema/pings.ts index cbdfdad..acde260 100644 --- a/src/server/db/schema/pings.ts +++ b/src/server/db/schema/pings.ts @@ -1,9 +1,4 @@ -import { - real, - timestamp, - varchar, - pgTable, -} from "drizzle-orm/pg-core"; +import { real, timestamp, varchar, pgTable } from "drizzle-orm/pg-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import { z } from "zod"; import { devices } from "./devices"; @@ -11,6 +6,7 @@ import { users } from "@/server/db/schema/auth"; import { type getPings } from "@/lib/api/pings/queries"; import { randomUUID } from "crypto"; +import { relations } from "drizzle-orm"; export const pings = pgTable("pings", { id: varchar("id", { length: 191 }) @@ -26,7 +22,12 @@ export const pings = pgTable("pings", { .references(() => users.id, { onDelete: "cascade" }) .notNull(), }); - +export const pingRelations = relations(pings, ({ one }) => ({ + device: one(devices, { + fields: [pings.deviceId], + references: [devices.id], + }), +})); // Schema for pings - used to validate API requests export const insertPingSchema = createInsertSchema(pings); diff --git a/src/server/db/seed/auth.ts b/src/server/db/seed/auth.ts new file mode 100644 index 0000000..7e2f816 --- /dev/null +++ b/src/server/db/seed/auth.ts @@ -0,0 +1,31 @@ +import { db } from ".."; +import { accounts } from "../schema/auth"; + +const runAuthSeed = async () => { + console.log("🔐", "Seeding auth"); + await db + .insert(accounts) + .values([ + { + userId: "2250f34e-997a-44de-ab8d-beddeda13525", + provider: "google", + type: "oauth", + providerAccountId: "112561477626832751929", + access_token: "FARTS", + expires_at: 9, + token_type: "Bearer", + scope: + "openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile", + id_token: "FARTS", + }, + ]) + .execute(); + + console.log("🌱", "Seeded auth"); + process.exit(0); +}; +runAuthSeed().catch((err) => { + console.error("❌ Auth seeding failed"); + console.error(err); + process.exit(1); +}); diff --git a/src/server/db/seed/seed.ts b/src/server/db/seed/seed.ts new file mode 100644 index 0000000..77fb09b --- /dev/null +++ b/src/server/db/seed/seed.ts @@ -0,0 +1,80 @@ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import { db } from ".."; +import { faker } from "@faker-js/faker"; +import { users } from "../schema/auth"; +import { children } from "../schema/children"; +import { devices } from "../schema/devices"; +import { pings } from "../schema/pings"; + +console.log("seed", "Seeding"); +const runSeed = async () => { + console.log("👯", "Seeding users"); + const seedUsers = [ + { + id: "2250f34e-997a-44de-ab8d-beddeda13525", + name: "Fergal Moran", + email: "fergal.moran@gmail.com", + emailVerified: new Date(), + }, + ]; + await db.insert(users).values(seedUsers).execute(); + + const seedChildren = [ + { + id: "2250f34e-997a-44de-ab8d-beddeda13525", + name: "Lil Debuggles", + phone: "123 456 789", + email: "lildebuggles@kidarr.com", + avatar: faker.image.avatar(), + userId: seedUsers[0]?.id!, + }, + ]; + await db.insert(children).values(seedChildren).execute(); + + const seedDevices = [ + { + id: "5af79a30-df27-4646-9d9f-77e19b4191c1", + name: "Not an iPhone", + deviceId: "373791e3-afe3-49de-b0a2-842a44071585", + childId: seedChildren[0]?.id!, + userId: seedUsers[0]?.id!, + deviceName: "Not an iPhone", + apiKey: "nQhXtqemsWjzBpbDxlIV2qtDx9xxO4oZVBJADdhJLfA=", + pin: 1234, + expires: new Date(2065), + }, + ]; + await db.insert(devices).values(seedDevices).execute(); + + const seedPings = [ + { + deviceId: seedDevices[0]?.id!, + userId: seedUsers[0]?.id!, + latitude: 51.903614, + longitude: -8.468399, + timestamp: new Date(), + }, + { + deviceId: seedDevices[0]?.id!, + userId: seedUsers[0]?.id!, + latitude: 51.8985, + longitude: -8.4756, + timestamp: new Date(), + }, + { + deviceId: seedDevices[0]?.id!, + userId: seedUsers[0]?.id!, + latitude: 51.93588161110811, + longitude: -8.495129534566756, + timestamp: new Date(), + }, + ]; + await db.insert(pings).values(seedPings).execute(); + console.log("🌱", "Seeded users"); + process.exit(0); +}; +runSeed().catch((err) => { + console.error("❌ Seeding failed"); + console.error(err); + process.exit(1); +});