diff --git a/docs/notes_tmp.html b/docs/notes_tmp.html deleted file mode 100644 index bd3dd6f..0000000 --- a/docs/notes_tmp.html +++ /dev/null @@ -1,2873 +0,0 @@ - - -
-Next.js Authentication Made Easy with Microsoft Entra ID
-Next.js 16 Middleware DEPRECATED - Authentication In Proxy Or Data Access Layer?
-Overview of user consent and how to manage it in Microsoft Entra | Microsoft
-Add Tailwind CSS to an Existing Next js Project
-How to style Material UI component with Tailwindcss in React project
-How to Use Prisma 7 in Next.js
-The following is the desire structure
-web-calibre/
-βββ src/
-β βββ app/ # All Routes (Pages & APIs)
-β β βββ api/ # API ROUTES
-β β β βββ auth/ # NextAuth routes [...nextauth]
-β β β βββ upload/ # Custom API for OneDrive sessions
-β β βββ dashboard/ # Protected user area
-β β βββ layout.tsx # Global Layout
-β β βββ page.tsx # Landing Page
-β βββ components/ # MUI Components (FileTable, Navbar)
-β βββ lib/ # Config (Prisma, Theme, Auth)
-β β βββ prisma.ts
-β β βββ theme.ts <-- MUI Theme Defined Here
-β βββ middleware.ts <-- Protected Routes Logic
-βββ prisma/ # DB Schema
-βββ .env # Secrets
-
-WebCalibre2 -Application (client) ID = 'b549931a-f491-436b-b7bc-d37d8ca3c17e'
-Object ID ='0b8256a2-e2ac-472b-896a-4b5845bc32fe'
-Directory (tenant) ID = '1c06ce7c-7884-4796-8652-d4c32d75a5d0'
-Expires = 1/3/2028
-Value='O3p8Q~oMph-0kSLwkvzEJzJdx_iHGOjJjrr5Pa1A'
-Secret ID='6a242be1-c711-4cc3-a132-03c1f57993ed'
-
Since you are using NextAuth.js, the URL must follow a very specific pattern.
-For local development: http://localhost:3000/api/auth/callback/azure-ad
-For production: https://your-domain.com/api/auth/callback/azure-ad
-Note: Replace azure-ad with whatever ID you give the provider in your code. By default, in NextAuth, it is usually azure-ad.
-WebCalibre3
-| Display name | -WebCalibre3 | -
|---|---|
| Application (client) ID | -a95ef644-cb9d-465c-8dae-f822a78a8ac3 | -
| Object ID | -1e1f2bda-661c-4ad0-9aae-82313c603076 | -
| Directory (tenant) ID. | -1c06ce7c-7884-4796-8652-d4c32d75a5d0 | -
Client credentials
-| Description | -Expires | -Value | -Secret ID | -
|---|---|---|---|
| WebCalibre3 | -1/5/2028 | -VZ48Q~EOsgZcgBI25cc0zys.h9zM7hN9I7DhZdnW | -d647cf28-80ed-45fb-a89e-e2bc323a40e9 | -

The following command creates the nextjs folder structure
-Yes, Next.js has a specific syntax to create a project in your current directory instead of creating a new subfolder.
-To do this, navigate to your empty project folder in the terminal and run:
-
-npx create-next-app@latest .
-
-Important Details:
-The Dot (.): This tells the installer to use the current directory as the project root.
-Naming Conflict: If the folder name contains capital letters (e.g., WebCalibre), the command will fail with an error. Next.js requires the root folder name to be all lowercase and URL-friendly. You must rename your folder to web-calibre before running the command. -This caused me rename to all lower case.
-3.Empty Folder: The directory must be empty (except for hidden files like .git). If you already have files in there, the installer will stop to prevent overwriting your work.
-What happens next? -Even though you are using the current directory, the interactive prompt will still ask you:
-"What is your project named?" You can just hit Enter to accept the current folder name.
-Configuration: It will then ask you about TypeScript, ESLint, Tailwind CSS, the src/ directory, and the App Router.
-Based on our previous discussions, I recommend selecting "Yes" for TypeScript, ESLint, Tailwind, the src/ directory, and the App Router to match the professional structure we planned for WebCalibre.
-In Next.js, the distinction between .env and .env.local is primarily about security and overriding defaults. -While both store key-value pairs, they are handled differently by Git and the Next.js runtime.
-The Next.js Loading Order -Next.js looks for variables in this specific order (from highest priority to lowest):
-DATABASE_URL="postgresql://stephen:Web2025$$@192.168.1.210:5432/webcalibre2"
-
-# Credentials for your Microsoft App (NextAuth)
-AZURE_AD_CLIENT_ID="549931a-f491-436b-b7bc-d37d8ca3c17e"
-AZURE_AD_CLIENT_SECRET="6a242be1-c711-4cc3-a132-03c1f57993ed"
-AZURE_AD_TENANT_ID="1c06ce7c-7884-4796-8652-d4c32d75a5d0"
-
-Summary Comparison Table
-| Feature | -.env | -.env.local | -
|---|---|---|
| Purpose | -Shared defaults/constants | -Private secrets & local overrides | -
| Committed to Git? | -Yes | -No (Added to .gitignore) | -
| Sensitive Data? | -NEVER | -YES (API Keys, Secrets) | -
| Scope | -All developers/environments | -Just your machine | -
**Best Practice Tip: ** Since .env.local isn't shared on GitHub, itβs a good idea to create a file named .env.example in your project. This file should contain the names of the keys (e.g., AZURE_AD_CLIENT_SECRET=) but leave the values blank, so other developers know which variables they need to create on their own machines.
-Nextjs initializes git by default. But is doesn't add some configs that I do
-git config --global push.followTags true
-git remote add origin "/Users/stephenlohning/Library/CloudStorage/OneDrive-Personal/Documents/10_GIT_Repositories/124_WebCalibre2.git"
-git remote -v -origin /Users/stephenlohning/Library/CloudStorage/OneDrive-Personal/Documents/10_GIT_Repositories/124_WebCalibre2.git (fetch) -origin /Users/stephenlohning/Library/CloudStorage/OneDrive-Personal/Documents/10_GIT_Repositories/124_WebCalibre2.git (push)
-npm run dev
-
-> 124_webcalibre2@0.1.0 dev
-> next dev
-
-β² Next.js 16.1.1 (Turbopack)
-- Local: http://localhost:3000
-- Network: http://192.168.1.100:3000
-- Environments: .env
-
-β Starting...
-β Ready in 648ms
-
-If it working proceed
-I ran through the Video:-
-Next.js Authentication Made Easy with Microsoft Entra ID
-npm install next-auth@beta
-Setup Environment -The only environment variable that is mandatory is the AUTH_SECRET. This is a random value used by the library to encrypt tokens and email verification hashes. (See Deployment to learn more). You can generate one via the official Auth.js CLI running:
-npx auth secret
-created .env.local
-AUTH_SECRET="7cz3Z4kUI2kB3mdAPo58iioUDLSRJ92X8+boEixvO8k=" # Added by `npx auth`. Read more: https://cli.authjs.dev
-
-# values generated by Gemini
-# Generated for security
-AUTH_SECRET="7cz3Z4kUI2kB3mdAPo58iioUDLSRJ92X8+boEixvO8k="
-
-# Use 'common' for multi-tenant (Work + Personal) support
-AUTH_MICROSOFT_ENTRA_ID_TENANT_ID="common"
-
-# The Application (client) ID
-AUTH_MICROSOFT_ENTRA_ID_ID="b549931a-f491-436b-b7bc-d37d8ca3c17e"
-
-# The Client Secret VALUE (ensure no leading '6' or spaces)
-AUTH_MICROSOFT_ENTRA_ID_SECRET="O3p8Q~oMph-0kSLwkvzEJzJdx_iHGOjJjrr5Pa1A"
-
-# Required to tell Auth.js to trust your localhost/proxy URL
-AUTH_TRUST_HOST=true
-
-DATABASE_URL="postgresql://stephen:Web2025$$@192.168.1.210:5432/webcalibre2"
-
-# Credentials for your Microsoft App (NextAuth)
-AZURE_AD_CLIENT_ID="549931a-f491-436b-b7bc-d37d8ca3c17e"
-AZURE_AD_CLIENT_SECRET="6a242be1-c711-4cc3-a132-03c1f57993ed"
-AZURE_AD_TENANT_ID="1c06ce7c-7884-4796-8652-d4c32d75a5d0"
-
-To get Prisma fully installed and ready to talk to your PostgreSQL database, you need to install the CLI (for migrations) and the Client (for your code to query the database).
-Run these three commands in your terminal:
-npm install prisma --save-dev
-##The Client is used by your Next.js code to talk to the DB
-npm install @prisma/client
-
-
-npm install dotenv-cli --save-dev
-
-Bash
-npx prisma init
-
-npx dotenv -e .env.local -- npx prisma generate -4. Apply your Schema to PostgreSQL -Finally, run this to actually create the tables in your webcalibre2 database:
-npx dotenv -e .env.local -- npx prisma migrate dev --name init_database
-To avoid typing that long dotenv command every time, open your package.json and add this to the "scripts" section:
-JSON
-"scripts": { -"db:migrate": "dotenv -e .env.local -- prisma migrate dev", -"db:studio": "dotenv -e .env.local -- prisma studio" -} -Now, you can just run npm run db:migrate whenever you update your schema!
-We can authenticate with MS Azure, upload a file and store it on my personal OneDrive. -I can see a few problems,
-if the file gets stored with its original filename, if I store the same file again it tells that it fail, but in reality it did not fail the it got renamed with 1.pdf added this is not added data base.
-We may be better to create a folder and put the file in the folder, to me the logical name would id of the file, but that is automatically create and assigned when the data is stored as a UUID.
-We would have to create the UUID ourselves, so that we can create a folder with that specific UUID and store the file in the folder.
-The Upload file Page needs to be able to create folder as well select a parent , add a description of either the file
-The functionality works may not the best user interface
-Projects ID 0371fdfe-a2d1-4f25-b229-804f299bc064 -Project-1 ID c8eebe0e-6e90-4cfb-8e44-c05f7064f3b1 parentID 0371fdfe-a2d1-4f25-b229-804f299bc064 -AS-NZS3000-2018.pdf parentID c8eebe0e-6e90-4cfb-8e44-c05f7064f3b1
-Now we will go now and add metadata
-Now we have the metadata along with some great filters on the data board page
-Now we are going to implement an an update of a file or folder stored. 12/1/2026
-For download there 2 function we need to implement
-down load the file to ~/user/Downloads, this should work for any file type including pdf
-if the file is a pdf open the file in another tab in the browser as most browser support reading a pdf
-For download there 2 function we need to implement
-down load the file to ~/user/Downloads, this should work for any file type including pdf
-if the file is a pdf open the file in another tab in the browser as most browser support reading a pdf
-So we need to implement 2 new actions, downLoad file and open pdf to read. Does this make sense ?
-A Next.js (App Router) dashboard for managing a OneDrive-synced file library. -Uses Prisma + PostgreSQL for metadata storage and MUI X v8 (DataGrid) for the frontend.
-slots.showToolbar prop on the <DataGrid />.
-expanded prop on the <QuickFilter /> component within the custom toolbar to ensure it doesn't collapse.slotProps.textField (not input) to customize the search input in MUI v7/v8.metadata_search) with width: 0.valueGetter to return JSON.stringify(row.metadata).initialState.columns.columnVisibilityModel.sharp and pdf-parse are Node.js-based and will crash if imported into client components.| Command | -Purpose | -
|---|---|
npx tsc --noEmit |
-Run this frequently. Validates types across the entire project. Finds errors your IDE might miss. | -
npx prisma generate |
-Updates the Prisma Client types after schema changes. | -
npx prisma db push |
-Pushes schema changes to the PostgreSQL database. | -
npm run dev |
-Starts the development server. | -
src/lib/metadata-extractor.ts.syncOneDrive Server Action.pdf-parse), EPUB (node-epub-utils), Images (sharp).Cmd + Shift + R or Ctrl + F5.gridArea: '1 / 1'. If icons overlap, check the styled-components logic in dashboard-view.tsx.1.AUIAfM4GHIR4lkeGUtTDLXWl0BqTSbWR9GtDt7zTfYyjwX6kAElCAA.BQABAwIAAAADAOz_BQD0_yyFlWKgjcwr_ed5f58Tdi53aj4fqbWRmjTI7vE5M94UQZT5SZLBxnTN7Psa7w-KJCMfY3AAzGgJzfOXOHGXSySvCzC6IlHwXL7uXLZYf84jdhgNEP2F1E1nz9ecmsG4tfjvMzwNPsh9nlY48Cz_qh_KDLu2odNesttqW7Om3MxsXLakskvMXynX07lJT6gbXT9OMoFk_F3M4Dz9DoYJD6CACZPdKaYqMeesfPt-B6Yj7tq1q9L4RUc6Fbrsew9chMqg1vurMlggcX1aBU7LF6JP50B3IkOB4qXrGDJtxM61gF64EXRay5UYVtWMh09et7SX4FcLZlHTyxpsvjSxaKxfpY9DFY2y0dhp0Ce5dZXoK7pplQwcxsv-dCeZmk4A3dqvAdxM6p0XNrowlOawpAlWqHoIC1ZmESDpyxueP0rlpfD9h3wkWcVAG9xOS_d-W9Z3y1G9PgBycOysvQTufueLs4PCjjId9-5AI4UaecQyUtpRCJnOxp23_e5ROLduihcDMrV87D8_ZN6nl49jFqr7kQZHbLanuVht18vSUyk31c9OtkFmC4p48_1xoIe5sYlPjoUd9Misgef7EziwdzapRyr649kb4Em_XMmJf_Y5zVDpT6NXRqlQAw3AhP53Px12hQsGsFkd0kzDBuwUSrKlda5E2lom4Wg5uXwb4zLD5DA6xwKfxBrAYICZT8QQYG5fTHjSP_2t9gs_K9-1-7UqKTb2VWGYf25QVnhgBXL1KbzosmFBOKjr31XjYo8IbgMjuEDGP-YfmyYOvmRg2Lx-VNlu8gTPcdSsdIivFSvEvHdUmIeReHMIdKjr0ZgVYwTtzG7QBs-8UXRvNo6IWecZnHEjx0QSLztyNXicgeOmT4gKDeJ0YT6rkWkNg_B8fYpx3j11ceRKpgQzm2E7UalGgBsbAHA1a4jnM7NrDv4TY_A_LRTabHPVrHpQWZ2HOJi5UlTQLv8xbiiJf94HVlxwncJd6YPy7ARjhvIA5nE5IiZJ8ZSUFwg4waCFibV-NDqiglo7YBCfx3DOrLsNjuufYQnjog_IDi6ac-DG7ulKS01nmAq7WfvshiW_mXLmwtPVusAcmLcfD-40B3kh12Cv-_EXSzfaVPP7M1Obi13TyoQDeVl-pMJuEC68xHzqA78SmZR8WluDRAJ5kOLBggvPbuHRFpkqtvFG8sGNzOqiBkq30qUErfk2Ao-SnHAh3fa3CAM2aYf3LdzXHh2_qGoVnrKdMfnUI14H6vrTxxBpDUinYejr8rLzbjH6tjDYWvjen5-BrDzOjazI7_ctS8zdLugG093kbWlYDW26FgYnKtdPCi-7F-emm8HGD82V_5e7Jc1qVs1G_D5eu0ohb_B1ky_AmxgCE8oL9Bdx1ehHnijNCarubf_MWMcloAv0XNxvrU4PoXdb7PgVBpYsKRT7tdnOGpokQj2n2xQSHvQVypd8xkHYloczdlmi9zvytM_Da8jnLdE0Td3mlnI-jlovrbzykD4YhS_ZcwIc_8c_WiHDeC3yAeTTx0EhuNh89HFSsmfod2BBaRgvf5SRpNwUjxd0PROvrGD_Hl0emtsC9YWdfULQBk3SKqJMV0x7Zv_Erj8JZoMzXbe8jv5bM4AmYqOPqznkXtAWAkedqLVYug0od9AgMIn-FFPpH4AvmHhhliMevIw74yEXCj28dxlHBZvyyqpbEwp8vr_kTjarjyp3s1p6QsYEoQ82vx-nQPiVFwWeGDsJcSmUD4FbtVIVCGY3p2cCtZHyRRWAK2LGgO32v3DhK7FSLKV-3f0LAafqdpZyGnqoF7pwn_RcpZz8l9tGctFl8Xd50Y1naLz8F08SlbJ197xdjzHLQ8r4jsVn5m9BkGbR0Kv3olXGRmtr5dkqGI-68_FCjkkqpwfOHg
-1.AUIAfM4GHIR4lkeGUtTDLXWl0BqTSbWR9GtDt7zTfYyjwX6kAElCAA.BQABAwIAAAADAOz_BQD0_yyFlWKgjcwr_ed5f58Tdi53aj4fqbWRmjTI7vE5M94UQZT5SZLBxnTN7Psa7w-KJCMfY3AAzGgJzfOXOHGXSySvCzC6IlHwXL7uXLZYf84jdhgNEP2F1E1nz9ecmsG4tfjvMzwNPsh9nlY48Cz_qh_KDLu2odNesttqW7Om3MxsXLakskvMXynX07lJT6gbXT9OMoFk_F3M4Dz9DoYJD6CACZPdKaYqMeesfPt-B6Yj7tq1q9L4RUc6Fbrsew9chMqg1vurMlggcX1aBU7LF6JP50B3IkOB4qXrGDJtxM61gF64EXRay5UYVtWMh09et7SX4FcLZlHTyxpsvjSxaKxfpY9DFY2y0dhp0Ce5dZXoK7pplQwcxsv-dCeZmk4A3dqvAdxM6p0XNrowlOawpAlWqHoIC1ZmESDpyxueP0rlpfD9h3wkWcVAG9xOS_d-W9Z3y1G9PgBycOysvQTufueLs4PCjjId9-5AI4UaecQyUtpRCJnOxp23_e5ROLduihcDMrV87D8_ZN6nl49jFqr7kQZHbLanuVht18vSUyk31c9OtkFmC4p48_1xoIe5sYlPjoUd9Misgef7EziwdzapRyr649kb4Em_XMmJf_Y5zVDpT6NXRqlQAw3AhP53Px12hQsGsFkd0kzDBuwUSrKlda5E2lom4Wg5uXwb4zLD5DA6xwKfxBrAYICZT8QQYG5fTHjSP_2t9gs_K9-1-7UqKTb2VWGYf25QVnhgBXL1KbzosmFBOKjr31XjYo8IbgMjuEDGP-YfmyYOvmRg2Lx-VNlu8gTPcdSsdIivFSvEvHdUmIeReHMIdKjr0ZgVYwTtzG7QBs-8UXRvNo6IWecZnHEjx0QSLztyNXicgeOmT4gKDeJ0YT6rkWkNg_B8fYpx3j11ceRKpgQzm2E7UalGgBsbAHA1a4jnM7NrDv4TY_A_LRTabHPVrHpQWZ2HOJi5UlTQLv8xbiiJf94HVlxwncJd6YPy7ARjhvIA5nE5IiZJ8ZSUFwg4waCFibV-NDqiglo7YBCfx3DOrLsNjuufYQnjog_IDi6ac-DG7ulKS01nmAq7WfvshiW_mXLmwtPVusAcmLcfD-40B3kh12Cv-_EXSzfaVPP7M1Obi13TyoQDeVl-pMJuEC68xHzqA78SmZR8WluDRAJ5kOLBggvPbuHRFpkqtvFG8sGNzOqiBkq30qUErfk2Ao-SnHAh3fa3CAM2aYf3LdzXHh2_qGoVnrKdMfnUI14H6vrTxxBpDUinYejr8rLzbjH6tjDYWvjen5-BrDzOjazI7_ctS8zdLugG093kbWlYDW26FgYnKtdPCi-7F-emm8HGD82V_5e7Jc1qVs1G_D5eu0ohb_B1ky_AmxgCE8oL9Bdx1ehHnijNCarubf_MWMcloAv0XNxvrU4PoXdb7PgVBpYsKRT7tdnOGpokQj2n2xQSHvQVypd8xkHYloczdlmi9zvytM_Da8jnLdE0Td3mlnI-jlovrbzykD4YhS_ZcwIc_8c_WiHDeC3yAeTTx0EhuNh89HFSsmfod2BBaRgvf5SRpNwUjxd0PROvrGD_Hl0emtsC9YWdfULQBk3SKqJMV0x7Zv_Erj8JZoMzXbe8jv5bM4AmYqOPqznkXtAWAkedqLVYug0od9AgMIn-FFPpH4AvmHhhliMevIw74yEXCj28dxlHBZvyyqpbEwp8vr_kTjarjyp3s1p6QsYEoQ82vx-nQPiVFwWeGDsJcSmUD4FbtVIVCGY3p2cCtZHyRRWAK2LGgO32v3DhK7FSLKV-3f0LAafqdpZyGnqoF7pwn_RcpZz8l9tGctFl8Xd50Y1naLz8F08SlbJ197xdjzHLQ8r4jsVn5m9BkGbR0Kv3olXGRmtr5dkqGI-68_FCjkkqpwfOHg
-1.AUIAfM4GHIR4lkeGUtTDLXWl0BqTSbWR9GtDt7zTfYyjwX6kAElCAA.BQABAwIAAAADAOz_BQD0_yyFlWKgjcwr_ed5f58Tdi53aj4fqbWRmjTI7vE5M94UQZT5SZLBxnTN7Psa7w-KJCMfY3AAzGgJzfOXOHGXSySvCzC6IlHwXL7uXLZYf84jdhgNEP2F1E1nz9ecmsG4tfjvMzwNPsh9nlY48Cz_qh_KDLu2odNesttqW7Om3MxsXLakskvMXynX07lJT6gbXT9OMoFk_F3M4Dz9DoYJD6CACZPdKaYqMeesfPt-B6Yj7tq1q9L4RUc6Fbrsew9chMqg1vurMlggcX1aBU7LF6JP50B3IkOB4qXrGDJtxM61gF64EXRay5UYVtWMh09et7SX4FcLZlHTyxpsvjSxaKxfpY9DFY2y0dhp0Ce5dZXoK7pplQwcxsv-dCeZmk4A3dqvAdxM6p0XNrowlOawpAlWqHoIC1ZmESDpyxueP0rlpfD9h3wkWcVAG9xOS_d-W9Z3y1G9PgBycOysvQTufueLs4PCjjId9-5AI4UaecQyUtpRCJnOxp23_e5ROLduihcDMrV87D8_ZN6nl49jFqr7kQZHbLanuVht18vSUyk31c9OtkFmC4p48_1xoIe5sYlPjoUd9Misgef7EziwdzapRyr649kb4Em_XMmJf_Y5zVDpT6NXRqlQAw3AhP53Px12hQsGsFkd0kzDBuwUSrKlda5E2lom4Wg5uXwb4zLD5DA6xwKfxBrAYICZT8QQYG5fTHjSP_2t9gs_K9-1-7UqKTb2VWGYf25QVnhgBXL1KbzosmFBOKjr31XjYo8IbgMjuEDGP-YfmyYOvmRg2Lx-VNlu8gTPcdSsdIivFSvEvHdUmIeReHMIdKjr0ZgVYwTtzG7QBs-8UXRvNo6IWecZnHEjx0QSLztyNXicgeOmT4gKDeJ0YT6rkWkNg_B8fYpx3j11ceRKpgQzm2E7UalGgBsbAHA1a4jnM7NrDv4TY_A_LRTabHPVrHpQWZ2HOJi5UlTQLv8xbiiJf94HVlxwncJd6YPy7ARjhvIA5nE5IiZJ8ZSUFwg4waCFibV-NDqiglo7YBCfx3DOrLsNjuufYQnjog_IDi6ac-DG7ulKS01nmAq7WfvshiW_mXLmwtPVusAcmLcfD-40B3kh12Cv-_EXSzfaVPP7M1Obi13TyoQDeVl-pMJuEC68xHzqA78SmZR8WluDRAJ5kOLBggvPbuHRFpkqtvFG8sGNzOqiBkq30qUErfk2Ao-SnHAh3fa3CAM2aYf3LdzXHh2_qGoVnrKdMfnUI14H6vrTxxBpDUinYejr8rLzbjH6tjDYWvjen5-BrDzOjazI7_ctS8zdLugG093kbWlYDW26FgYnKtdPCi-7F-emm8HGD82V_5e7Jc1qVs1G_D5eu0ohb_B1ky_AmxgCE8oL9Bdx1ehHnijNCarubf_MWMcloAv0XNxvrU4PoXdb7PgVBpYsKRT7tdnOGpokQj2n2xQSHvQVypd8xkHYloczdlmi9zvytM_Da8jnLdE0Td3mlnI-jlovrbzykD4YhS_ZcwIc_8c_WiHDeC3yAeTTx0EhuNh89HFSsmfod2BBaRgvf5SRpNwUjxd0PROvrGD_Hl0emtsC9YWdfULQBk3SKqJMV0x7Zv_Erj8JZoMzXbe8jv5bM4AmYqOPqznkXtAWAkedqLVYug0od9AgMIn-FFPpH4AvmHhhliMevIw74yEXCj28dxlHBZvyyqpbEwp8vr_kTjarjyp3s1p6QsYEoQ82vx-nQPiVFwWeGDsJcSmUD4FbtVIVCGY3p2cCtZHyRRWAK2LGgO32v3DhK7FSLKV-3f0LAafqdpZyGnqoF7pwn_RcpZz8l9tGctFl8Xd50Y1naLz8F08SlbJ197xdjzHLQ8r4jsVn5m9BkGbR0Kv3olXGRmtr5dkqGI-68_FCjkkqpwfOHg
- -export async function getMetadataPreviewAction(fileId: string) {
- const session = await auth();
- if (!session?.user?.id) throw new Error("Unauthorized");
-
- console.log(`\n--- π Magic Fill Started for File ID: ${fileId} ---`);
-
- try {
- const node = await getFileNodeById(fileId);
- if (!node || !node.oneDriveId) throw new Error("File not found");
-
- // 1. Get the token from our utility
- const token = await getFreshAccessToken(session.user.id);
-
- // LOG: Just check the length to be sure it's not empty
- console.log(`π Token retrieved (Length: ${token.length})`);
-
- console.log(`π‘ Fetching binary for: ${node.name}...`);
-
- // 2. Fetch the content from Microsoft Graph
- const response = await fetch(
- `https://graph.microsoft.com/v1.0/me/drive/items/${node.oneDriveId}/content`,
- {
- headers: {
- 'Authorization': `Bearer ${token}`,
- 'Accept': '*/*'
- },
- }
- );
-
- if (!response.ok) {
- // If it fails here, we'll see the real reason from Microsoft
- const errorText = await response.text();
- console.error("β Microsoft Graph Error Response:", errorText);
- throw new Error(`OneDrive Download Failed: ${response.status}`);
- }
-
- const arrayBuffer = await response.arrayBuffer();
- const buffer = Buffer.from(arrayBuffer);
- console.log(`π¦ Success! Downloaded ${buffer.length} bytes.`);
-
- // 3. Extract Metadata
- const extractedData = await extractMetadata(buffer, node.name);
-
- console.log("β
RAW DATA EXTRACTED:");
- console.dir(extractedData, { depth: null, colors: true });
-
- return { success: true, data: extractedData };
-
- } catch (error: any) {
- console.error("β Magic Fill Error:", error.message);
- return { success: false, error: error.message };
- }
-}
-
-npm run dev
-
-> 124_webcalibre2@0.1.0 dev
-> next dev
-
-β² Next.js 16.1.1 (Turbopack)
-- Local: http://localhost:3000
-- Network: http://192.168.1.100:3000
-- Environments: .env.local, .env
-- Experiments (use with caution):
- Β· serverActions
-
-β Starting...
-β Ready in 380ms
-β
Prisma 7 connected to PostgreSQL successfully
- GET /dashboard 200 in 643ms (compile: 139ms, proxy.ts: 115ms, render: 388ms)
-β
Prisma 7 connected to PostgreSQL successfully
- GET /update/d3ccd68a-0493-487d-9273-5fa8ff6a6f9a 200 in 329ms (compile: 274ms, proxy.ts: 19ms, render: 37ms)
-
---- π Magic Fill Started for File ID: d3ccd68a-0493-487d-9273-5fa8ff6a6f9a ---
-π Access token expired. Refreshing for user: cc6f11ff-549d-40fa-883f-7886ee6487cf
-β
Prisma 7 connected to PostgreSQL successfully
-π Token retrieved (Length: 1484)
-π‘ Fetching binary for: IMG_2360.jpeg...
-π¦ Success! Downloaded 1233596 bytes.
-πΈ FULL RAW EXIF DATA: {
- "bigEndian": true,
- "Image": {
- "Make": "Apple",
- "Model": "iPhone 14",
- "Orientation": 1,
- "XResolution": 72,
- "YResolution": 72,
- "ResolutionUnit": 2,
- "Software": "18.6.2",
- "DateTime": "2025-10-03T08:02:39.000Z",
- "HostComputer": "iPhone 14",
- "YCbCrPositioning": 1,
- "ExifTag": 228,
- "GPSTag": 2586
- },
- "Thumbnail": {
- "Compression": 6,
- "XResolution": 72,
- "YResolution": 72,
- "ResolutionUnit": 2,
- "JPEGInterchangeFormat": 2990,
- "JPEGInterchangeFormatLength": 3322
- },
- "Photo": {
- "ExposureTime": 0.025,
- "FNumber": 1.5,
- "ExposureProgram": 2,
- "ISOSpeedRatings": 500,
- "ExifVersion": {
- "type": "Buffer",
- "data": [
- 48,
- 50,
- 51,
- 50
- ]
- },
- "DateTimeOriginal": "2025-10-03T08:02:39.000Z",
- "DateTimeDigitized": "2025-10-03T08:02:39.000Z",
- "OffsetTime": "+02:00",
- "OffsetTimeOriginal": "+02:00",
- "OffsetTimeDigitized": "+02:00",
- "ComponentsConfiguration": {
- "type": "Buffer",
- "data": [
- 1,
- 2,
- 3,
- 0
- ]
- },
- "ShutterSpeedValue": 5.327015336217066,
- "ApertureValue": 1.1699250021066825,
- "BrightnessValue": -0.4198113650227582,
- "ExposureBiasValue": 0,
- "MeteringMode": 5,
- "Flash": 16,
- "FocalLength": 5.7,
- "SubjectArea": [
- 2006,
- 1506,
- 2213,
- 1327
- ],
- "MakerNote": {
- "type": "Buffer",
- "data": [
- 65,
- 112,
- 112,
- 108,
- 101,
- 32,
- 105,
- 79,
- 83,
- 0,
- 0,
- 1,
- 77,
- 77,
- 0,
- 49,
- 0,
- 1,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 15,
- 0,
- 2,
- 0,
- 7,
- 0,
- 0,
- 2,
- 0,
- 0,
- 0,
- 2,
- 96,
- 0,
- 3,
- 0,
- 7,
- 0,
- 0,
- 0,
- 104,
- 0,
- 0,
- 4,
- 96,
- 0,
- 4,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 5,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 149,
- 0,
- 6,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 152,
- 0,
- 7,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 8,
- 0,
- 10,
- 0,
- 0,
- 0,
- 3,
- 0,
- 0,
- 4,
- 200,
- 0,
- 12,
- 0,
- 10,
- 0,
- 0,
- 0,
- 2,
- 0,
- 0,
- 4,
- 224,
- 0,
- 13,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 39,
- 0,
- 14,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 16,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 20,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 12,
- 0,
- 22,
- 0,
- 7,
- 0,
- 0,
- 0,
- 72,
- 0,
- 0,
- 4,
- 240,
- 0,
- 23,
- 0,
- 16,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 5,
- 56,
- 0,
- 25,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 32,
- 2,
- 0,
- 26,
- 0,
- 2,
- 0,
- 0,
- 0,
- 6,
- 0,
- 0,
- 5,
- 64,
- 0,
- 31,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 32,
- 0,
- 2,
- 0,
- 0,
- 0,
- 37,
- 0,
- 0,
- 5,
- 70,
- 0,
- 33,
- 0,
- 10,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 5,
- 107,
- 0,
- 35,
- 0,
- 9,
- 0,
- 0,
- 0,
- 2,
- 0,
- 0,
- 5,
- 115,
- 0,
- 37,
- 0,
- 16,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 5,
- 123,
- 0,
- 38,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 3,
- 0,
- 39,
- 0,
- 10,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 5,
- 131,
- 0,
- 43,
- 0,
- 2,
- 0,
- 0,
- 0,
- 37,
- 0,
- 0,
- 5,
- 139,
- 0,
- 45,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 30,
- 150,
- 0,
- 46,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 47,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 48,
- 0,
- 48,
- 0,
- 10,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 5,
- 176,
- 0,
- 54,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 42,
- 0,
- 55,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 4,
- 0,
- 58,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 59,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 60,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 4,
- 0,
- 63,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 64,
- 0,
- 7,
- 0,
- 0,
- 0,
- 74,
- 0,
- 0,
- 5,
- 184,
- 0,
- 65,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 67,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 68,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 69,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 70,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 74,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 2,
- 0,
- 77,
- 0,
- 10,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 6,
- 2,
- 0,
- 78,
- 0,
- 7,
- 0,
- 0,
- 0,
- 121,
- 0,
- 0,
- 6,
- 10,
- 0,
- 79,
- 0,
- 7,
- 0,
- 0,
- 0,
- 43,
- 0,
- 0,
- 6,
- 131,
- 0,
- 82,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 11,
- 0,
- 83,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 1,
- 0,
- 85,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 88,
- 0,
- 9,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 7,
- 3,
- 0,
- 0,
- 0,
- 0,
- 239,
- 1,
- 205,
- 1,
- 169,
- 1,
- 135,
- 1,
- 103,
- 1,
- 73,
- 1,
- 45,
- 1,
- 19,
- 1,
- 251,
- 0,
- 228,
- 0,
- 207,
- 0,
- 188,
- 0,
- 172,
- 0,
- 158,
- 0,
- 146,
- 0,
- 136,
- 0,
- 54,
- 2,
- 17,
- 2,
- 232,
- 1,
- 193,
- 1,
- 155,
- 1,
- 118,
- 1,
- 85,
- 1,
- 55,
- 1,
- 26,
- 1,
- 0,
- 1,
- 231,
- 0,
- 209,
- 0,
- 189,
- 0,
- 173,
- 0,
- 160,
- 0,
- 147,
- 0,
- 125,
- 2,
- 89,
- 2,
- 47,
- 2,
- 2,
- 2,
- 216,
- 1,
- 175,
- 1,
- 137,
- 1,
- 99,
- 1,
- 64,
- 1,
- 33,
- 1,
- 4,
- 1,
- 233,
- 0,
- 210,
- 0,
- 190,
- 0,
- 173,
- 0,
- 159,
- 0,
- 8,
- 3,
- 217,
- 2,
- 170,
- 2,
- 120,
- 2,
- 65,
- 2,
- 13,
- 2,
- 218,
- 1,
- 171,
- 1,
- 125,
- 1,
- 83,
- 1,
- 44,
- 1,
- 10,
- 1,
- 235,
- 0,
- 210,
- 0,
- 188,
- 0,
- 170,
- 0,
- 252,
- 2,
- 242,
- 2,
- 210,
- 2,
- 170,
- 2,
- 131,
- 2,
- 81,
- 2,
- 30,
- 2,
- 230,
- 1,
- 175,
- 1,
- 124,
- 1,
- 77,
- 1,
- 36,
- 1,
- 255,
- 0,
- 225,
- 0,
- 199,
- 0,
- 178,
- 0,
- 69,
- 1,
- 93,
- 1,
- 121,
- 1,
- 168,
- 1,
- 208,
- 1,
- 206,
- 1,
- 193,
- 1,
- 168,
- 1,
- 135,
- 1,
- 107,
- 1,
- 69,
- 1,
- 32,
- 1,
- 251,
- 0,
- 220,
- 0,
- 196,
- 0,
- 176,
- 0,
- 183,
- 0,
- 185,
- 0,
- 182,
- 0,
- 183,
- 0,
- 181,
- 0,
- 182,
- 0,
- 175,
- 0,
- 179,
- 0,
- 192,
- 0,
- 183,
- 0,
- 178,
- 0,
- 169,
- 0,
- 158,
- 0,
- 143,
- 0,
- 139,
- 0,
- 131,
- 0,
- 173,
- 0,
- 179,
- 0,
- 173,
- 0,
- 185,
- 0,
- 187,
- 0,
- 231,
- 0,
- 145,
- 1,
- 164,
- 1,
- 147,
- 1,
- 107,
- 1,
- 38,
- 1,
- 157,
- 0,
- 139,
- 0,
- 166,
- 0,
- 169,
- 0,
- 152,
- 0,
- 36,
- 0,
- 55,
- 0,
- 64,
- 0,
- 58,
- 0,
- 57,
- 0,
- 55,
- 0,
- 56,
- 0,
- 50,
- 0,
- 45,
- 0,
- 42,
- 0,
- 37,
- 0,
- 27,
- 0,
- 25,
- 0,
- 23,
- 0,
- 30,
- 0,
- 43,
- 0,
- 10,
- 0,
- 11,
- 0,
- 12,
- 0,
- 14,
- 0,
- 19,
- 0,
- 23,
- 0,
- 25,
- 0,
- 24,
- 0,
- 23,
- 0,
- 21,
- 0,
- 20,
- 0,
- 18,
- 0,
- 17,
- 0,
- 16,
- 0,
- 16,
- 0,
- 16,
- 0,
- 6,
- 0,
- 7,
- 0,
- 7,
- 0,
- 9,
- 0,
- 11,
- 0,
- 9,
- 0,
- 12,
- 0,
- 12,
- 0,
- 15,
- 0,
- 13,
- 0,
- 13,
- 0,
- 12,
- 0,
- 11,
- 0,
- 10,
- 0,
- 9,
- 0,
- 10,
- 0,
- 4,
- 0,
- 5,
- 0,
- 6,
- 0,
- 6,
- 0,
- 7,
- 0,
- 9,
- 0,
- 9,
- 0,
- 9,
- 0,
- 9,
- 0,
- 9,
- 0,
- 10,
- 0,
- 9,
- 0,
- 9,
- 0,
- 6,
- 0,
- 7,
- 0,
- 7,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 4,
- 0,
- 5,
- 0,
- 6,
- 0,
- 6,
- 0,
- 6,
- 0,
- 7,
- 0,
- 8,
- 0,
- 8,
- 0,
- 8,
- 0,
- 6,
- 0,
- 5,
- 0,
- 5,
- 0,
- 5,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 6,
- 0,
- 6,
- 0,
- 4,
- 0,
- 5,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 5,
- 0,
- 4,
- 0,
- 5,
- 0,
- 3,
- 0,
- 5,
- 0,
- 6,
- 0,
- 7,
- 0,
- 6,
- 0,
- 5,
- 0,
- 4,
- 0,
- 5,
- 0,
- 5,
- 0,
- 4,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 3,
- 0,
- 3,
- 0,
- 4,
- 0,
- 4,
- 0,
- 5,
- 0,
- 98,
- 112,
- 108,
- 105,
- 115,
- 116,
- 48,
- 48,
- 212,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 7,
- 8,
- 85,
- 102,
- 108,
- 97,
- 103,
- 115,
- 85,
- 118,
- 97,
- 108,
- 117,
- 101,
- 89,
- 116,
- 105,
- 109,
- 101,
- 115,
- 99,
- 97,
- 108,
- 101,
- 85,
- 101,
- 112,
- 111,
- 99,
- 104,
- 16,
- 1,
- 19,
- 0,
- 1,
- 62,
- 142,
- 239,
- 162,
- 85,
- 109,
- 18,
- 59,
- 154,
- 202,
- 0,
- 16,
- 0,
- 8,
- 17,
- 23,
- 29,
- 39,
- 45,
- 47,
- 56,
- 61,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 9,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 63,
- 255,
- 255,
- 84,
- 169,
- 0,
- 0,
- 168,
- 221,
- 0,
- 0,
- 54,
- 115,
- 0,
- 9,
- 142,
- 158,
- 255,
- 255,
- 244,
- 153,
- 0,
- 2,
- 69,
- 230,
- 0,
- 0,
- 0,
- 91,
- 0,
- 0,
- 0,
- 128,
- 0,
- 0,
- 0,
- 81,
- 0,
- 0,
- 1,
- 0,
- 98,
- 112,
- 108,
- 105,
- 115,
- 116,
- 48,
- 48,
- 95,
- 16,
- 28,
- 65,
- 88,
- 118,
- 67,
- 67,
- 48,
- 55,
- 76,
- 49,
- 117,
- 79,
- 73,
- 113,
- 48,
- 73,
- 98,
- 101,
- 51,
- 76,
- 121,
- 112,
- 112,
- 98,
- 98,
- 86,
- 43,
- 108,
- 113,
- 8,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 39,
- 0,
- 0,
- 0,
- 0,
- 0,
- 80,
- 32,
- 4,
- 113,
- 55,
- 53,
- 48,
- 110,
- 0,
- 55,
- 67,
- 65,
- 54,
- 53,
- 50,
- 48,
- 68,
- 45,
- 67,
- 50,
- 49,
- 55,
- 45,
- 52,
- 56,
- 68,
- 49,
- 45,
- 56,
- 48,
- 49,
- 67,
- 45,
- 50,
- 69,
- 49,
- 57,
- 54,
- 69,
- 66,
- 66,
- 68,
- 52,
- 65,
- 50,
- 0,
- 0,
- 0,
- 249,
- 65,
- 0,
- 1,
- 39,
- 154,
- 0,
- 0,
- 1,
- 55,
- 16,
- 0,
- 0,
- 27,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 20,
- 142,
- 0,
- 2,
- 96,
- 195,
- 0,
- 0,
- 19,
- 224,
- 51,
- 67,
- 53,
- 51,
- 52,
- 69,
- 66,
- 69,
- 45,
- 52,
- 65,
- 53,
- 51,
- 45,
- 52,
- 55,
- 69,
- 70,
- 45,
- 57,
- 49,
- 55,
- 65,
- 45,
- 66,
- 55,
- 56,
- 53,
- 57,
- 55,
- 51,
- 55,
- 50,
- 53,
- 57,
- 52,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 98,
- 112,
- 108,
- 105,
- 115,
- 116,
- 48,
- 48,
- 212,
- 1,
- 2,
- 3,
- 4,
- 5,
- 6,
- 6,
- 7,
- 81,
- 51,
- 81,
- 49,
- 81,
- 50,
- 81,
- 48,
- 16,
- 0,
- 34,
- 0,
- 0,
- 0,
- 0,
- 16,
- 1,
- 8,
- 17,
- 19,
- 21,
- 23,
- 25,
- 27,
- 32,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 8,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 34,
- 0,
- 2,
- 38,
- 42,
- 0,
- 0,
- 19,
- 59,
- 98,
- 112,
- 108,
- 105,
- 115,
- 116,
- 48,
- 48,
- 210,
- 1,
- 2,
- 3,
- 4,
- 81,
- 49,
- 81,
- 50,
- 16,
- 3,
- 162,
- 5,
- 10,
- 210,
- 6,
- 7,
- 8,
- 9,
- 83,
- 50,
- 46,
- 49,
- 83,
- 50,
- 46,
- 50,
- 35,
- 64,
- 157,
- 225,
- 32,
- 96,
- 0,
- 0,
- 0,
- 35,
- 64,
- 124,
- 160,
- 0,
- 0,
- 0,
- 0,
- 0,
- 210,
- 6,
- 7,
- 11,
- 12,
- 35,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 35,
- 64,
- 72,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 8,
- 13,
- 15,
- 17,
- 19,
- 22,
- 27,
- 31,
- 35,
- 44,
- 53,
- 58,
- 67,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 13,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 76,
- 98,
- 112,
- 108,
- 105,
- 115,
- 116,
- 48,
- 48,
- 16,
- 0,
- 8,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 1,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 0,
- 10
- ]
- },
- "SubSecTimeOriginal": "714",
- "SubSecTimeDigitized": "714",
- "FlashpixVersion": {
- "type": "Buffer",
- "data": [
- 48,
- 49,
- 48,
- 48
- ]
- },
- "ColorSpace": 65535,
- "PixelXDimension": 4032,
- "PixelYDimension": 3024,
- "SensingMethod": 2,
- "SceneType": {
- "type": "Buffer",
- "data": [
- 1
- ]
- },
- "ExposureMode": 0,
- "WhiteBalance": 0,
- "FocalLengthIn35mmFilm": 26,
- "SceneCaptureType": 0,
- "LensSpecification": [
- 1.5399999618512084,
- 5.699999809263318,
- 1.5,
- 2.4
- ],
- "LensMake": "Apple",
- "LensModel": "iPhone 14 back dual wide camera 5.7mm f/1.5",
- "CompositeImage": 2
- },
- "GPSInfo": {
- "GPSLatitudeRef": "N",
- "GPSLatitude": [
- 42,
- 42,
- 22.91
- ],
- "GPSLongitudeRef": "W",
- "GPSLongitude": [
- 7,
- 1,
- 50.76
- ],
- "GPSAltitudeRef": 0,
- "GPSAltitude": 1265.3340248962656,
- "GPSTimeStamp": [
- 6,
- 2,
- 39
- ],
- "GPSSpeedRef": "K",
- "GPSSpeed": 0,
- "GPSImgDirectionRef": "T",
- "GPSImgDirection": 147.2551498127341,
- "GPSDestBearingRef": "T",
- "GPSDestBearing": 147.2551498127341,
- "GPSDateStamp": "2025:10:03",
- "GPSHPositioningError": 4.748651528267963
- }
-}
-β
RAW DATA EXTRACTED:
-{
- type: 'IMAGE (JPEG)',
- dimensions: '4032x3024',
- title: 'IMG_2360.jpeg'
-}
- POST /update/d3ccd68a-0493-487d-9273-5fa8ff6a6f9a 200 in 4.0s (compile: 9ms, proxy.ts: 19ms, render: 4.0s)
-
-Two questions
-https://graph.microsoft.com/v1.0/me/drive/items/${node.oneDriveId}/content,
-{
-headers: {
-'Authorization': Bearer ${token},
-'Accept': '/'
-},
-}
-);
-Should this be in the services/onedrive.ts to be compliant with the Data Access Layer (DAL) that nextjs recomments?Those are excellent architectural questions. You're moving from "making it work" to "making it scale," which is exactly where you want to be.
-Yes, it absolutely belongs in services/onedrive.ts.
Next.js (and general clean architecture) recommends a Data Access Layer (DAL) to decouple your business logic from the specific implementation of your data source.
-Why this is better:
-services/onedrive.ts) instead of hunting down fetch calls in your API routes.Recommended structure for services/onedrive.ts:
export async function getOneDriveFileBuffer(oneDriveId: string, token: string): Promise<Buffer> {
- const response = await fetch(
- `https://graph.microsoft.com/v1.0/me/drive/items/${oneDriveId}/content`,
- {
- headers: {
- 'Authorization': `Bearer ${token}`,
- 'Accept': '*/*'
- },
- }
- );
-
- if (!response.ok) {
- throw new Error(`OneDrive download failed: ${response.statusText}`);
- }
-
- const arrayBuffer = await response.arrayBuffer();
- return Buffer.from(arrayBuffer);
-}
-
-
-getMetadataFromJson?lib/metadata-extractor.ts is the perfect spot.
In the Next.js ecosystem, the lib/ directory is the standard convention for "Internal Libraries" or "Utilities"βcode that is purely functional, doesn't depend on React, and performs a specific technical task (like parsing a buffer).
Why this is the right move:
-Structure for lib/metadata-extractor.ts:
import exifReader from 'exif-reader';
-
-// Define an interface for your metadata to keep your Json field typed
-export interface FileMetadata {
- deviceModel?: string;
- capturedAt?: Date;
- lat?: number;
- lng?: number;
- [key: string]: any; // Allows for other fields
-}
-
-export function extractExifMetadata(buffer: Buffer): FileMetadata {
- // logic goes here...
-}
-
-
-When you put these in their proper places, your API Route or Server Action becomes very readable "glue" code:
-services/onedrive.ts)lib/metadata-extractor.ts)Would you like me to help you refine the internal logic of the extractExifMetadata function to ensure it handles the specific "Rational" numbers (fractions) that EXIF often uses for exposure and GPS?