diff --git a/.env.sample b/.env.sample index cc9f943..9789cbd 100644 --- a/.env.sample +++ b/.env.sample @@ -1,2 +1,3 @@ NEXT_PUBLIC_IMMICH_URL="" -IMMICH_API_KEY="" \ No newline at end of file +IMMICH_API_KEY="" +DATABASE_URL="" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7b078fa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use an official Node.js runtime as the base image +FROM node:18-alpine + +# Set the working directory in the container +WORKDIR /app + +# Copy package.json and package-lock.json to the working directory +COPY package*.json ./ + +# Install dependencies +RUN npm ci + +# Copy the rest of the application code +COPY . . + +# Build the Next.js application +RUN npm run build + +# Expose the port that Next.js uses +EXPOSE 3000 + +# Set the command to run the application +CMD ["npm", "start"] \ No newline at end of file diff --git a/bun.lockb b/bun.lockb index 07fe3ec..51cd29c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 5575252..a931d3d 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,10 @@ "clsx": "^2.1.1", "cmdk": "1.0.0", "date-fns": "^3.6.0", + "drizzle-orm": "^0.33.0", "lucide-react": "^0.428.0", "next": "14.2.5", + "pg": "^8.12.0", "qs": "^6.13.0", "react": "^18", "react-day-picker": "9.0.8", @@ -43,9 +45,11 @@ }, "devDependencies": { "@types/node": "^20", + "@types/pg": "^8.11.6", "@types/react": "^18", "@types/react-dom": "^18", "autoprefixer": "^10.4.20", + "drizzle-kit": "^0.24.0", "eslint": "^8", "eslint-config-next": "14.2.5", "postcss": "^8.4.41", diff --git a/src/config/db.ts b/src/config/db.ts new file mode 100644 index 0000000..717c9d7 --- /dev/null +++ b/src/config/db.ts @@ -0,0 +1,15 @@ +import { drizzle } from 'drizzle-orm/node-postgres'; + +import { Client } from "pg"; +import { ENV } from "./environment"; +import * as schema from "@/schema"; + +console.log(ENV.DATABASE_URL); +const client = new Client({ + connectionString: ENV.DATABASE_URL, +}); + +await client.connect(); +export const db = drizzle(client, { + schema +}); \ No newline at end of file diff --git a/src/config/environment.ts b/src/config/environment.ts index aa34b85..ff98582 100644 --- a/src/config/environment.ts +++ b/src/config/environment.ts @@ -1,4 +1,5 @@ export const ENV = { IMMICH_URL: process.env.NEXT_PUBLIC_IMMICH_URL as string, - IMMICH_API_KEY: process.env.IMMICH_API_KEY as string + IMMICH_API_KEY: process.env.IMMICH_API_KEY as string, + DATABASE_URL: process.env.DATABASE_URL as string, } \ No newline at end of file diff --git a/src/pages/api/hello.ts b/src/pages/api/hello.ts deleted file mode 100644 index ea77e8f..0000000 --- a/src/pages/api/hello.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from "next"; - -type Data = { - name: string; -}; - -export default function handler( - req: NextApiRequest, - res: NextApiResponse, -) { - res.status(200).json({ name: "John Doe" }); -} diff --git a/src/pages/api/stats/assets.ts b/src/pages/api/stats/assets.ts new file mode 100644 index 0000000..7129d66 --- /dev/null +++ b/src/pages/api/stats/assets.ts @@ -0,0 +1,21 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import { db } from "@/config/db"; +import { assets } from "@/schema"; +import { count } from "drizzle-orm"; +import type { NextApiRequest, NextApiResponse } from "next"; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + try { + const assetsFromDB = await db.select({ + count: count() + }).from(assets).groupBy(assets.createdAt); + res.status(200).json(assetsFromDB); + } catch (error) { + res.status(500).json({ + name: "Error", + }); + } +} diff --git a/src/schema/asset.schema.ts b/src/schema/asset.schema.ts new file mode 100644 index 0000000..b5e8a9e --- /dev/null +++ b/src/schema/asset.schema.ts @@ -0,0 +1,49 @@ +import { pgTable, uuid, varchar, timestamp, boolean } from 'drizzle-orm/pg-core'; + +export const assets = pgTable('assets', { + id: uuid('id').defaultRandom().primaryKey().notNull(), + deviceAssetId: varchar('deviceAssetId').notNull(), + // ownerId: uuid('ownerId').notNull().references(() => users.id, { onDelete: 'cascade', onUpdate: 'cascade' }), + deviceId: varchar('deviceId').notNull(), + type: varchar('type').notNull(), + originalPath: varchar('originalPath').notNull(), + previewPath: varchar('previewPath'), + fileCreatedAt: timestamp('fileCreatedAt', { withTimezone: true }).notNull(), + fileModifiedAt: timestamp('fileModifiedAt', { withTimezone: true }).notNull(), + isFavorite: boolean('isFavorite').default(false).notNull(), + duration: varchar('duration'), + thumbnailPath: varchar('thumbnailPath').default(''), + encodedVideoPath: varchar('encodedVideoPath').default(''), + // checksum: bytea('checksum').notNull(), + isVisible: boolean('isVisible').default(true).notNull(), + livePhotoVideoId: uuid('livePhotoVideoId').references((): any => assets.id, { onDelete: 'set null', onUpdate: 'cascade' }), + updatedAt: timestamp('updatedAt', { withTimezone: true }).defaultNow().notNull(), + createdAt: timestamp('createdAt', { withTimezone: true }).defaultNow().notNull(), + isArchived: boolean('isArchived').default(false).notNull(), + originalFileName: varchar('originalFileName').notNull(), + sidecarPath: varchar('sidecarPath'), + // thumbhash: bytea('thumbhash'), + isOffline: boolean('isOffline').default(false).notNull(), + // libraryId: uuid('libraryId').references(() => libraries.id, { onDelete: 'cascade', onUpdate: 'cascade' }), + isExternal: boolean('isExternal').default(false).notNull(), + deletedAt: timestamp('deletedAt', { withTimezone: true }), + localDateTime: timestamp('localDateTime', { withTimezone: true }).notNull(), + // stackId: uuid('stackId').references(() => assetStack.id, { onDelete: 'set null', onUpdate: 'cascade' }), + duplicateId: uuid('duplicateId'), +}); + +// // Note: You'll need to define these referenced tables as well +// export const users = pgTable('users', { +// id: uuid('id').primaryKey(), +// // ... other columns +// }); + +// export const libraries = pgTable('libraries', { +// id: uuid('id').primaryKey(), +// // ... other columns +// }); + +// export const assetStack = pgTable('asset_stack', { +// id: uuid('id').primaryKey(), +// // ... other columns +// }); \ No newline at end of file diff --git a/src/schema/index.ts b/src/schema/index.ts new file mode 100644 index 0000000..2f7ae62 --- /dev/null +++ b/src/schema/index.ts @@ -0,0 +1 @@ +export { assets } from './asset.schema'; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index fb68dc1..78a0434 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,7 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, + "target": "ESNext", "paths": { "@/*": ["./src/*"] }