惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

有赞技术团队
有赞技术团队
量子位
B
Blog RSS Feed
Schneier on Security
Schneier on Security
L
LINUX DO - 最新话题
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
N
News | PayPal Newsroom
阮一峰的网络日志
阮一峰的网络日志
Microsoft Security Blog
Microsoft Security Blog
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
大猫的无限游戏
大猫的无限游戏
PCI Perspectives
PCI Perspectives
aimingoo的专栏
aimingoo的专栏
D
Docker
T
The Exploit Database - CXSecurity.com
Last Week in AI
Last Week in AI
W
WeLiveSecurity
Stack Overflow Blog
Stack Overflow Blog
月光博客
月光博客
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
J
Java Code Geeks
O
OpenAI News
C
Cisco Blogs
Hacker News - Newest:
Hacker News - Newest: "LLM"
爱范儿
爱范儿
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Threat Research - Cisco Blogs
Cisco Talos Blog
Cisco Talos Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Help Net Security
Help Net Security
Scott Helme
Scott Helme
The Hacker News
The Hacker News
Y
Y Combinator Blog
A
Arctic Wolf
V
V2EX
P
Proofpoint News Feed
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
S
Securelist
G
Google Developers Blog
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog

Learn Cloud Native

Agentgateway rate limiting for agents | Learn Cloud Native Local development with coding agents on Kubernetes using Signadot | Learn Cloud Native cuenv: one typed file for your whole project | Learn Cloud Native Preflight: AI Code Review Before You Push Anatomy of AI Agents Deploying to Fly.io using Dagger and Github Top Cloud-Native & Kubernetes Certifications [2026 Guide] Rapid microservices development with Signadot How to prepare for Istio certified associate exam (ICA) Global Rate Limiting in Istio with Envoy Rate Limit Service My Journey with Istio: From Incubation to Graduation Cilium Network Policy Tutorial: Secure Kubernetes Step by Step Kubernetes Networking: How kube-proxy and iptables Work Istio ServiceEntry: DNS vs. STATIC Resolution & Endpoints Explained Apply an Istio DestinationRule Globally (Mesh-Wide) Istio Rate Limiting: Configure a Local Rate Limiter in Envoy How to expose custom ports on Istio ingress gateway Portainer Tutorial: A Web UI for Kubernetes & Containers Traefik Proxy 2.x and TLS 101 Kubernetes CLI (kubectl) tips you didn't know about Setting up SSL certificates with Istio Gateway ArgoCD Best Practices You Should Know 在 OCI Ampere A1 计算实例上运行 AI Running AI On OCI Ampere A1 Instance How to Deploy Traefik Proxy Using Flux and GitOps Principles Firebase Emulators with Next.js: Local Setup Guide Running Hugo on free Ampere VM (Oracle Cloud Infrastructure) How to use kwatch to detect crashes in Kubernetes clusters Continuous profiling in Kubernetes using Pyroscope Monitoring containers with cAdvisor Creating a Kubernetes cluster in Google Cloud (LAB) Your first Kubernetes Pod and ReplicaSet (LABS) Container Lifecycle Hooks Maybe Convert Wasm Extension Config? GetIstio - CLI, training, and community Attach multiple VirtualServices to Istio Gateway Kubernetes Volumes Explained: Keep Data Beyond the Pod Send a Slack message when Docker images are updated Kubernetes Network Policy Ambassador Container Pattern Start Kubernetes Release Sidecar Container Pattern Kubernetes Init Containers Deploying multiple Istio Ingress Gateways Branch by Abstraction Pattern The Strangler Pattern Kubernetes Development Environment with Skaffold Securing Kubernetes Ingress with Ambassador and Let's Encrypt All About the Ingress Resource How to quarantine Kubernetes pods? Getting started with Kubernetes Horizontal partitioning in MongoDB Docker image tagging scheme Six things to keep in mind when working with Dockerfiles Beginners guide to Docker Beginners guide to gateways and proxies Deploy and Operate Multiple Istio Meshes in one Kubernetes Cluster Managing service meshes with Meshery Circuit Breaking in Istio Explained Build and push your Docker images using Github Actions Kubernetes and Istio service mesh workshop materials Build Netlify-like deployment for React app using Kubernetes pods Six exciting enhancements in Istio 1.4.0 Fallacies of Distributed Systems CAP Theorem Explained Master the Kubernetes CLI (kubectl) - Cheatsheet Minikube Basics and How to Get Started with Kubernetes 5 Tips to Be More Productive with Kubernetes What are sticky sessions and how to configure them with Istio? Debugging Kubernetes applications using Istio Kubernetes Ingress and Istio Gateway Resource Zero Downtime Releases using Kubernetes and Istio Traffic Mirroring with Istio Service Mesh Expose a Kubernetes service on your own custom domain
Accessing Google Drive from Next.js
Peter Jausovec · 2024-09-23 · via Learn Cloud Native

This weekend I was playing a bit with Google Drive, specifically getting access to the files and folders through Next.js. The idea came from a tweet where one could select files and folders, process them (i.e. export the files, chunk and encode them and then save them in vector DB), so the contents could be used as context when interacting with an LLM.

I got a quick prototype of this working with @v0, @AnthropicAI in a couple of hours 🤯 Still a lot of rough edges, but the basic functionality is there. pic.twitter.com/0LzJFf3Icm

— Peter Jausovec (@pjausovec) September 22, 2024

In this post I'll just explain the access to Google Drive part, but if there's interest, I can do another post on explaining how I've doine the processing part. Let's get started!

1. Next.js project and Google Drive API setup

I am going to assume you have a Next.js project set up or at least you know how to set up one (npx create-next-app@latest). If not, check the Next.js docs for more information on how to get started.

Since we're going to be accessing Google Drive, you'll need a GCP project with the Google Drive API enabled. Assuming you have a GCP account and project, you can search for "Google Drive API" and then enable it - it shouldn't take too long. Once enabled, click the "Credentials" from the sidebar and then create "OAuth client ID" credentials. Make sure you select "Web application" as the application type and add http://localhost:3000/api/auth/callback/google as an authorized redirect URI for development.

After you've added that and created the credentials, you'll get the Client ID and Client Secret which we'll use in the NextAuth.js setup. Put these values in the .env.local file in the root of your Next.js project:

GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret

2. Setting up NextAuth.js

First, let's set up NextAuth.js with Google provider support. Create a file called [...nextauth]/route.ts in the app/api/auth directory of your Next.js project:

// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import type { NextAuthOptions } from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      authorization: {
        params: {
          scope:
            'openid email profile https://www.googleapis.com/auth/drive.readonly',
          prompt: 'consent',
          access_type: 'offline',
          response_type: 'code',
        },
      },
    }),
  ],
  callbacks: {
    async jwt({ token, account, profile }) {
      if (account) {
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        token.expiresAt = account.expires_at;
      }
      if (profile) {
        token.id = profile.sub;
      }
      return token;
    },
    async session({ session, token }) {
      session.accessToken = token.accessToken as string;
      session.refreshToken = token.refreshToken as string;
      session.expiresAt = token.expiresAt as number;
      if (session.user) {
        session.user.id = token.id as string;
      }
      return session;
    },
  },
  session: {
    strategy: 'jwt',
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

This setup it more or less a default setup for NextAuth.js with Google provider, but in our case we are additionally requesting the https://www.googleapis.com/auth/drive.readonly scope. Note I am also setting the access_type to offline, so I can refresh the token later on in the backend.

3. Creating an API route to fetch Google Drive folders

Now, let's create an API route to fetch the folder structure from Google Drive. Create a new file called folders.ts in the app/api directory:

import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
import { google } from 'googleapis';

async function fetchFolderContents(
  drive: any,
  folderId: string = 'root'
): Promise<any[]> {
  const response = await drive.files.list({
    q: `'${folderId}' in parents`,
    fields: 'files(id, name, mimeType, fileExtension, size, iconLink)',
  });

  const items = await Promise.all(
    response.data.files.map(async (file: any) => {
      const item: any = {
        id: file.id,
        name: file.name,
        type:
          file.mimeType === 'application/vnd.google-apps.folder'
            ? 'folder'
            : 'file',
        extension: file.fileExtension,
        size: file.size,
        icon: file.iconLink,
      };

      if (item.type === 'folder') {
        item.children = await fetchFolderContents(drive, file.id);
      }

      return item;
    })
  );

  return items;
}

export async function GET(req: NextRequest) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET });

  if (!token || !token.accessToken) {
    return NextResponse.json({ error: 'Not authenticated' }, { status: 401 });
  }

  try {
    const auth = new google.auth.OAuth2(
      process.env.GOOGLE_CLIENT_ID,
      process.env.GOOGLE_CLIENT_SECRET
    );

    auth.setCredentials({
      access_token: token.accessToken as string,
      refresh_token: token.refreshToken as string,
      expiry_date: token.expiresAt
        ? (token.expiresAt as number) * 1000
        : undefined,
    });

    const drive = google.drive({ version: 'v3', auth });
    const structure = await fetchFolderContents(drive);

    return NextResponse.json({ structure });
  } catch (error) {
    console.error('Error fetching folder structure:', error);
    return NextResponse.json(
      { error: 'Failed to fetch folder structure' },
      { status: 500 }
    );
  }
}

In this API route I am using the Google Drive SDK to recursively fetch the folder contents. To initialize the SDK, I am using the OAuth2 class, instantiating it with the client and secret we got from the GCP project. Then, I am attaching the access and refresh token that was sent from the frontend when the user authenticated.

Using the SDK is fairly straighh forward - using the list function to get the files and folders in a specific folder (folderId), specifying the fields I want to get back, and then recursively fetching the children if the item is a folder.

4. Implementing the frontend component

With the authentication and API route set up, let's create a React component to display the folder structure and allow users to select files. I am using shadcn here for the components.

'use client';

import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { ScrollArea } from '@/components/ui/scroll-area';

interface Item {
  id: string;
  name: string;
  type: 'folder' | 'file';
  extension?: string;
  children?: Item[];
  size?: number;
  icon?: string;
}

export default function GoogleDriveComponent() {
  const { data: session, status } = useSession();
  const [structure, setStructure] = useState<Item[]>([]);
  const [selectedItems, setSelectedItems] = useState<Item[]>([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const fetchStructure = async () => {
      if (status === 'authenticated') {
        try {
          setIsLoading(true);
          const response = await fetch('/api/folders');
          if (!response.ok) throw new Error('Failed to fetch folder structure');
          const data = await response.json();
          setStructure(data.structure);
        } catch (error) {
          console.error('Error fetching folder structure:', error);
        } finally {
          setIsLoading(false);
        }
      }
    };

    fetchStructure();
  }, [session, status]);

  const toggleItemSelection = (item: Item) => {
    setSelectedItems((prev) => {
      const index = prev.findIndex((i) => i.id === item.id);
      if (index > -1) {
        return prev.filter((i) => i.id !== item.id);
      } else {
        return [...prev, item];
      }
    });
  };

  const renderItem = (item: Item) => (
    <li key={item.id} className="mb-1">
      <div className="flex items-center space-x-2">
        <Checkbox
          checked={selectedItems.some((i) => i.id === item.id)}
          onCheckedChange={() => toggleItemSelection(item)}
        />
        <span className="text-sm font-medium">{item.name}</span>
      </div>
      {item.type === 'folder' && item.children && (
        <ul className="pl-6 mt-1">
          {item.children.map((child) => renderItem(child))}
        </ul>
      )}
    </li>
  );

  if (status === 'loading' || isLoading) {
    return <div>Loading...</div>;
  }

  if (status === 'unauthenticated') {
    return <div>Please sign in to view folders</div>;
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Select Items from Google Drive</CardTitle>
      </CardHeader>
      <CardContent>
        <ScrollArea className="h-[300px] w-full border rounded-md p-4">
          <ul>{structure.map((item) => renderItem(item))}</ul>
        </ScrollArea>
        <div className="mt-4">
          <p>{selectedItems.length} item(s) selected</p>
          <Button onClick={() => console.log(selectedItems)}>
            Process Selected Files
          </Button>
        </div>
      </CardContent>
    </Card>
  );
}

This component fetches the folder structure when the user is authenticated, displays it as a tree, and allows users to select files and folders.

Gdrive in Next.js
Gdrive in Next.js

Conclusion

In this post, we've set up NextAuth.js with Google provider support, created an API route to fetch the folder structure from Google Drive, and built a simple React component to display the folder structure and allow users to select files. This is just a starting point, and you can extend this further by adding more features like file download, upload, and more.