mirror of
https://github.com/fergalmoran/kidarr-server.git
synced 2025-12-22 09:17:51 +00:00
Added pings
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
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();
|
||||
@@ -14,6 +15,8 @@ export const getChildren = async () => {
|
||||
.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: [] })) };
|
||||
};
|
||||
|
||||
58
src/lib/api/pings/mutations.ts
Normal file
58
src/lib/api/pings/mutations.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { db } from "@/server/db/index";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import {
|
||||
PingId,
|
||||
NewPingParams,
|
||||
UpdatePingParams,
|
||||
updatePingSchema,
|
||||
insertPingSchema,
|
||||
pings,
|
||||
pingIdSchema
|
||||
} from "@/server/db/schema/pings";
|
||||
import { getUserAuth } from "@/lib/auth/utils";
|
||||
|
||||
export const createPing = async (ping: NewPingParams) => {
|
||||
const { session } = await getUserAuth();
|
||||
const newPing = insertPingSchema.parse({ ...ping, userId: session?.user.id! });
|
||||
try {
|
||||
const [p] = await db.insert(pings).values(newPing).returning();
|
||||
return { ping: p };
|
||||
} catch (err) {
|
||||
const message = (err as Error).message ?? "Error, please try again";
|
||||
console.error(message);
|
||||
throw { error: message };
|
||||
}
|
||||
};
|
||||
|
||||
export const updatePing = async (id: PingId, ping: UpdatePingParams) => {
|
||||
const { session } = await getUserAuth();
|
||||
const { id: pingId } = pingIdSchema.parse({ id });
|
||||
const newPing = updatePingSchema.parse({ ...ping, userId: session?.user.id! });
|
||||
try {
|
||||
const [p] = await db
|
||||
.update(pings)
|
||||
.set(newPing)
|
||||
.where(and(eq(pings.id, pingId!), eq(pings.userId, session?.user.id!)))
|
||||
.returning();
|
||||
return { ping: p };
|
||||
} catch (err) {
|
||||
const message = (err as Error).message ?? "Error, please try again";
|
||||
console.error(message);
|
||||
throw { error: message };
|
||||
}
|
||||
};
|
||||
|
||||
export const deletePing = async (id: PingId) => {
|
||||
const { session } = await getUserAuth();
|
||||
const { id: pingId } = pingIdSchema.parse({ id });
|
||||
try {
|
||||
const [p] = await db.delete(pings).where(and(eq(pings.id, pingId!), eq(pings.userId, session?.user.id!)))
|
||||
.returning();
|
||||
return { ping: p };
|
||||
} catch (err) {
|
||||
const message = (err as Error).message ?? "Error, please try again";
|
||||
console.error(message);
|
||||
throw { error: message };
|
||||
}
|
||||
};
|
||||
|
||||
19
src/lib/api/pings/queries.ts
Normal file
19
src/lib/api/pings/queries.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { db } from "@/server/db/index";
|
||||
import { eq, and } from "drizzle-orm";
|
||||
import { getUserAuth } from "@/lib/auth/utils";
|
||||
import { type PingId, pingIdSchema, pings } from "@/server/db/schema/pings";
|
||||
import { devices } from "@/server/db/schema/devices";
|
||||
|
||||
export const getPings = async () => {
|
||||
const { session } = await getUserAuth();
|
||||
const p = await db.select({ ping: pings, device: devices }).from(pings).leftJoin(devices, eq(pings.deviceId, devices.id)).where(eq(pings.userId, session?.user.id!));
|
||||
return { pings: p };
|
||||
};
|
||||
|
||||
export const getPingById = async (id: PingId) => {
|
||||
const { session } = await getUserAuth();
|
||||
const { id: pingId } = pingIdSchema.parse({ id });
|
||||
const [p] = await db.select().from(pings).where(and(eq(pings.id, pingId), eq(pings.userId, session?.user.id!))).leftJoin(devices, eq(pings.deviceId, devices.id));
|
||||
return { ping: p };
|
||||
};
|
||||
|
||||
20
src/server/db/migrations/0002_abandoned_luckman.sql
Normal file
20
src/server/db/migrations/0002_abandoned_luckman.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
CREATE TABLE IF NOT EXISTS "pings" (
|
||||
"id" varchar(191) PRIMARY KEY NOT NULL,
|
||||
"latitude" real NOT NULL,
|
||||
"longitude" real NOT NULL,
|
||||
"timestamp" timestamp NOT NULL,
|
||||
"device_id" varchar(256) NOT NULL,
|
||||
"user_id" varchar(256) NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "pings" ADD CONSTRAINT "pings_device_id_devices_id_fk" FOREIGN KEY ("device_id") REFERENCES "devices"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "pings" ADD CONSTRAINT "pings_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE cascade ON UPDATE no action;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
425
src/server/db/migrations/meta/0002_snapshot.json
Normal file
425
src/server/db/migrations/meta/0002_snapshot.json
Normal file
@@ -0,0 +1,425 @@
|
||||
{
|
||||
"id": "34d0a88b-b9f0-4d72-a427-5f9a72104706",
|
||||
"prevId": "3520a784-4841-418f-8cf4-7e58249c843f",
|
||||
"version": "5",
|
||||
"dialect": "pg",
|
||||
"tables": {
|
||||
"account": {
|
||||
"name": "account",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"type": {
|
||||
"name": "type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"providerAccountId": {
|
||||
"name": "providerAccountId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"token_type": {
|
||||
"name": "token_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"id_token": {
|
||||
"name": "id_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"session_state": {
|
||||
"name": "session_state",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"account_userId_user_id_fk": {
|
||||
"name": "account_userId_user_id_fk",
|
||||
"tableFrom": "account",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {
|
||||
"account_provider_providerAccountId_pk": {
|
||||
"name": "account_provider_providerAccountId_pk",
|
||||
"columns": [
|
||||
"provider",
|
||||
"providerAccountId"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"children": {
|
||||
"name": "children",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(191)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"avatar": {
|
||||
"name": "avatar",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"children_user_id_user_id_fk": {
|
||||
"name": "children_user_id_user_id_fk",
|
||||
"tableFrom": "children",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"devices": {
|
||||
"name": "devices",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(191)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"device_id": {
|
||||
"name": "device_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"child_id": {
|
||||
"name": "child_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"devices_child_id_children_id_fk": {
|
||||
"name": "devices_child_id_children_id_fk",
|
||||
"tableFrom": "devices",
|
||||
"tableTo": "children",
|
||||
"columnsFrom": [
|
||||
"child_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"devices_user_id_user_id_fk": {
|
||||
"name": "devices_user_id_user_id_fk",
|
||||
"tableFrom": "devices",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"pings": {
|
||||
"name": "pings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "varchar(191)",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"latitude": {
|
||||
"name": "latitude",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"longitude": {
|
||||
"name": "longitude",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"timestamp": {
|
||||
"name": "timestamp",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"device_id": {
|
||||
"name": "device_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "varchar(256)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"pings_device_id_devices_id_fk": {
|
||||
"name": "pings_device_id_devices_id_fk",
|
||||
"tableFrom": "pings",
|
||||
"tableTo": "devices",
|
||||
"columnsFrom": [
|
||||
"device_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"pings_user_id_user_id_fk": {
|
||||
"name": "pings_user_id_user_id_fk",
|
||||
"tableFrom": "pings",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"session": {
|
||||
"name": "session",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"sessionToken": {
|
||||
"name": "sessionToken",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"userId": {
|
||||
"name": "userId",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"session_userId_user_id_fk": {
|
||||
"name": "session_userId_user_id_fk",
|
||||
"tableFrom": "session",
|
||||
"tableTo": "user",
|
||||
"columnsFrom": [
|
||||
"userId"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"user": {
|
||||
"name": "user",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"emailVerified": {
|
||||
"name": "emailVerified",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"image": {
|
||||
"name": "image",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {}
|
||||
},
|
||||
"verificationToken": {
|
||||
"name": "verificationToken",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"identifier": {
|
||||
"name": "identifier",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"token": {
|
||||
"name": "token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"expires": {
|
||||
"name": "expires",
|
||||
"type": "timestamp",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {
|
||||
"verificationToken_identifier_token_pk": {
|
||||
"name": "verificationToken_identifier_token_pk",
|
||||
"columns": [
|
||||
"identifier",
|
||||
"token"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uniqueConstraints": {}
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,13 @@
|
||||
"when": 1705877897654,
|
||||
"tag": "0001_sticky_frightful_four",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 2,
|
||||
"version": "5",
|
||||
"when": 1706464847221,
|
||||
"tag": "0002_abandoned_luckman",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { users, accounts, sessions, verificationTokens } from "./auth"
|
||||
import { children } from "./children";
|
||||
import { devices } from "./devices";
|
||||
import { pings } from "./pings";
|
||||
|
||||
export { devices, children, users, accounts, sessions, verificationTokens }
|
||||
export { pings, devices, children, users, accounts, sessions, verificationTokens }
|
||||
66
src/server/db/schema/pings.ts
Normal file
66
src/server/db/schema/pings.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
real,
|
||||
timestamp,
|
||||
varchar,
|
||||
pgTable,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema, createSelectSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
import { devices } from "./devices";
|
||||
import { users } from "@/server/db/schema/auth";
|
||||
import { type getPings } from "@/lib/api/pings/queries";
|
||||
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
export const pings = pgTable("pings", {
|
||||
id: varchar("id", { length: 191 })
|
||||
.primaryKey()
|
||||
.$defaultFn(() => randomUUID()),
|
||||
latitude: real("latitude").notNull(),
|
||||
longitude: real("longitude").notNull(),
|
||||
timestamp: timestamp("timestamp").notNull(),
|
||||
deviceId: varchar("device_id", { length: 256 })
|
||||
.references(() => devices.id, { onDelete: "cascade" })
|
||||
.notNull(),
|
||||
userId: varchar("user_id", { length: 256 })
|
||||
.references(() => users.id, { onDelete: "cascade" })
|
||||
.notNull(),
|
||||
});
|
||||
|
||||
// Schema for pings - used to validate API requests
|
||||
export const insertPingSchema = createInsertSchema(pings);
|
||||
|
||||
export const insertPingParams = createSelectSchema(pings, {
|
||||
latitude: z.coerce.number(),
|
||||
longitude: z.coerce.number(),
|
||||
timestamp: z.coerce.string().min(1),
|
||||
deviceId: z.coerce.string().min(1),
|
||||
}).omit({
|
||||
id: true,
|
||||
userId: true,
|
||||
});
|
||||
|
||||
export const updatePingSchema = createSelectSchema(pings);
|
||||
|
||||
export const updatePingParams = createSelectSchema(pings, {
|
||||
latitude: z.coerce.number(),
|
||||
longitude: z.coerce.number(),
|
||||
timestamp: z.coerce.string().min(1),
|
||||
deviceId: z.coerce.string().min(1),
|
||||
}).omit({
|
||||
userId: true,
|
||||
});
|
||||
|
||||
export const pingIdSchema = updatePingSchema.pick({ id: true });
|
||||
|
||||
// Types for pings - used to type API request params and within Components
|
||||
export type Ping = z.infer<typeof updatePingSchema>;
|
||||
export type NewPing = z.infer<typeof insertPingSchema>;
|
||||
export type NewPingParams = z.infer<typeof insertPingParams>;
|
||||
export type UpdatePingParams = z.infer<typeof updatePingParams>;
|
||||
export type PingId = z.infer<typeof pingIdSchema>["id"];
|
||||
|
||||
// this type infers the return from getPings() - meaning it will include any joins
|
||||
export type CompletePing = Awaited<
|
||||
ReturnType<typeof getPings>
|
||||
>["pings"][number];
|
||||
Reference in New Issue
Block a user