Skip to content

Instantly share code, notes, and snippets.

@dantetesta
Created November 24, 2025 11:24
Show Gist options
  • Select an option

  • Save dantetesta/82f418cdaafb604b443cf3e35b763f0c to your computer and use it in GitHub Desktop.

Select an option

Save dantetesta/82f418cdaafb604b443cf3e35b763f0c to your computer and use it in GitHub Desktop.
Google Drive Integration

🚀 Guia Completo de Integração Google Drive API

Autor: Dante Testa
Data: 24/11/2025
Versão: 1.0.0


📋 Índice

  1. Visão Geral
  2. Pré-requisitos
  3. Configuração no Google Cloud Console
  4. Instalação de Dependências
  5. Configuração do Laravel
  6. Autenticação OAuth 2.0
  7. GoogleDriveService
  8. Estrutura de Pastas
  9. Exemplos de Uso
  10. Segurança
  11. Troubleshooting

🎯 Visão Geral

Esta integração permite que aplicações Laravel gerenciem arquivos no Google Drive de forma profissional, incluindo:

  • ✅ Upload de arquivos (simples e resumable para arquivos grandes)
  • ✅ Download e streaming de arquivos
  • ✅ Criação e gerenciamento de estrutura de pastas
  • ✅ Autenticação OAuth 2.0 com refresh token
  • ✅ Controle de permissões e acesso privado
  • ✅ Integração com Laravel Storage (Flysystem)

Stack Utilizada

  • Laravel 11 (PHP 8.2+)
  • Google API Client (via Composer)
  • Flysystem Google Drive Adapter (masbug/flysystem-google-drive-ext)
  • OAuth 2.0 para autenticação

📦 Pré-requisitos

1. Conta Google Cloud

  • Acesso ao Google Cloud Console
  • Projeto criado no Google Cloud
  • Faturamento habilitado (não será cobrado para uso básico)

2. Ambiente Laravel

PHP >= 8.2
Laravel >= 11.0
Composer

⚙️ Configuração no Google Cloud Console

Passo 1: Criar Projeto

  1. Acesse Google Cloud Console
  2. Clique em "Selecionar projeto""Novo Projeto"
  3. Nome do projeto: DanteFlix (ou nome da sua aplicação)
  4. Clique em "Criar"

Passo 2: Habilitar Google Drive API

  1. No menu lateral, vá em "APIs e Serviços""Biblioteca"
  2. Busque por "Google Drive API"
  3. Clique em "Ativar"

Passo 3: Configurar Tela de Consentimento OAuth

  1. Vá em "APIs e Serviços""Tela de consentimento OAuth"
  2. Escolha "Externo" (para uso público) ou "Interno" (apenas G Suite)
  3. Preencha os campos obrigatórios:
  4. Clique em "Salvar e continuar"

Passo 4: Adicionar Escopos (Scopes)

  1. Na seção "Escopos", clique em "Adicionar ou remover escopos"
  2. Adicione o escopo:
    https://www.googleapis.com/auth/drive
    
  3. Clique em "Atualizar" e "Salvar e continuar"

Passo 5: Criar Credenciais OAuth 2.0

  1. Vá em "APIs e Serviços""Credenciais"
  2. Clique em "Criar credenciais""ID do cliente OAuth"
  3. Tipo de aplicativo: "Aplicativo da Web"
  4. Nome: DanteFlix Web Client
  5. URIs de redirecionamento autorizados:
    https://wprevolution.com.br/admin/settings/google-drive/callback
    http://localhost:8000/admin/settings/google-drive/callback
    
  6. Clique em "Criar"
  7. IMPORTANTE: Copie e salve:
    • Client ID (ex: 689791833607-xxxxx.apps.googleusercontent.com)
    • Client Secret (ex: GOCSPX-xxxxxxxxxxxxx)

📥 Instalação de Dependências

1. Instalar via Composer

composer require masbug/flysystem-google-drive-ext:^2.3

Esta biblioteca já inclui:

  • google/apiclient (Google API Client)
  • league/flysystem (Flysystem 3)

2. Verificar Instalação

composer show | grep google
composer show | grep flysystem

🔧 Configuração do Laravel

1. Variáveis de Ambiente (.env)

Adicione as seguintes variáveis no arquivo .env:

# Google Drive API
GOOGLE_DRIVE_CLIENT_ID=689791833607-xxxxx.apps.googleusercontent.com
GOOGLE_DRIVE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxx
GOOGLE_DRIVE_REDIRECT_URI=https://wprevolution.com.br/admin/settings/google-drive/callback
GOOGLE_DRIVE_SCOPES="https://www.googleapis.com/auth/drive"
GOOGLE_DRIVE_REFRESH_TOKEN=
GOOGLE_DRIVE_FOLDER_ROOT_ID=

Observações:

  • GOOGLE_DRIVE_REFRESH_TOKEN será preenchido após autenticação OAuth
  • GOOGLE_DRIVE_FOLDER_ROOT_ID é o ID da pasta raiz no Drive (opcional)

2. Configurar Filesystem (config/filesystems.php)

Adicione o disco google no array disks:

'disks' => [
    // ... outros discos

    'google' => [
        'driver' => 'google',
        'clientId' => env('GOOGLE_DRIVE_CLIENT_ID'),
        'clientSecret' => env('GOOGLE_DRIVE_CLIENT_SECRET'),
        'refreshToken' => env('GOOGLE_DRIVE_REFRESH_TOKEN'),
        'folder' => 'root', // ou ID de pasta específica
        'teamDriveId' => env('GOOGLE_DRIVE_TEAM_DRIVE_ID', null),
    ],
],

3. Criar Service Provider

Crie o arquivo app/Providers/GoogleDriveServiceProvider.php:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\Filesystem;
use Masbug\Flysystem\GoogleDriveAdapter;
use Google\Client as GoogleClient;

class GoogleDriveServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Storage::extend('google', function ($app, $config) {
            $client = new GoogleClient();
            $client->setClientId($config['clientId']);
            $client->setClientSecret($config['clientSecret']);
            
            // Usar refresh token para obter access token
            $client->fetchAccessTokenWithRefreshToken($config['refreshToken']);
            
            $service = new \Google_Service_Drive($client);
            
            // Usar o folder ID como raiz
            $folderId = $config['folder'] ?? 'root';
            
            $adapter = new GoogleDriveAdapter($service, $folderId);
            
            return new Filesystem($adapter);
        });
    }
}

4. Registrar Service Provider

Adicione no arquivo bootstrap/providers.php:

return [
    App\Providers\AppServiceProvider::class,
    App\Providers\GoogleDriveServiceProvider::class,
];

🔐 Autenticação OAuth 2.0

Fluxo de Autenticação

1. Usuário clica em "Conectar Google Drive"
2. Redireciona para Google OAuth
3. Usuário autoriza o app
4. Google redireciona de volta com código
5. App troca código por refresh token
6. Refresh token é salvo no .env

Controller de Autenticação

Veja implementação completa em: app/Http/Controllers/Admin/GoogleDriveSettingsController.php

Métodos principais:

  • connect() - Inicia autenticação OAuth
  • callback() - Recebe código e obtém refresh token
  • disconnect() - Remove autenticação

🛠️ GoogleDriveService

Classe principal para gerenciar arquivos: app/Services/GoogleDriveService.php

Métodos Principais

Upload de Arquivos

// Upload simples
$result = $googleDrive->uploadToFolder($file, $folderId, 'nome-arquivo.pdf');

// Upload com estrutura organizada
$result = $googleDrive->uploadToOrganizedFolder($file, 'Nome Download', 'categoria-slug');

Gerenciamento de Pastas

// Criar ou obter pasta
$folderId = $googleDrive->createOrGetFolder('Nome Pasta', $parentId);

// Criar estrutura de curso
$result = $googleDrive->createCourseStructure('Nome Curso', 'categoria-slug');

// Criar pasta de módulo
$result = $googleDrive->createModuleFolder('Módulo 1', $courseFolderId);

// Criar pasta de aula
$result = $googleDrive->createLessonFolder('Aula 1', $moduleFolderId);

Download e Streaming

// Download de arquivo
$result = $googleDrive->downloadFile($fileId);

// Streaming (para vídeos)
$result = $googleDrive->streamFile($fileId);

Deletar Arquivos/Pastas

// Deletar arquivo
$result = $googleDrive->deleteFile($fileId);

// Deletar pasta de curso
$result = $googleDrive->deleteCourseFolder($courseFolderId);

// Deletar pasta de download
$result = $googleDrive->deleteDownloadFolder($downloadName, $categorySlug);

Renomear Pastas

// Renomear pasta de curso
$result = $googleDrive->renameCourseFolder($folderId, 'Novo Nome');

// Renomear pasta de módulo
$result = $googleDrive->renameModuleFolder($folderId, 'Novo Nome');

// Renomear pasta de aula
$result = $googleDrive->renameLessonFolder($folderId, 'Novo Nome');

📁 Estrutura de Pastas

Exemplo de Estrutura Hierárquica

Google Drive (Raiz)
└── DanteFlix/
    ├── Cursos/
    │   ├── php-avancado/
    │   │   ├── anexos/
    │   │   │   ├── capa.jpg
    │   │   │   └── apresentacao.mp4
    │   │   ├── Modulo 1 - Introducao/
    │   │   │   ├── anexos/
    │   │   │   └── Aula 1 - Bem-vindo/
    │   │   │       ├── video.mp4
    │   │   │       └── slides.pdf
    │   │   └── Modulo 2 - POO/
    │   │       └── Aula 1 - Classes/
    │   │           └── video.mp4
    │   └── laravel-11/
    │       └── ...
    └── Downloads/
        ├── ebooks/
        │   └── PHP Moderno/
        │       └── php-moderno.pdf
        └── ferramentas/
            └── VSCode Setup/
                └── vscode-setup.zip

💡 Exemplos de Uso

1. Upload Simples via Laravel Storage

use Illuminate\Support\Facades\Storage;

// Upload via Flysystem
$path = $request->file('document')->store('/', 'google');

// Obter URL
$url = Storage::disk('google')->url($path);

2. Upload com GoogleDriveService

use App\Services\GoogleDriveService;

public function store(Request $request, GoogleDriveService $googleDrive)
{
    $file = $request->file('document');
    
    // Criar pasta se não existir
    $folderId = $googleDrive->createOrGetFolder('Documentos');
    
    // Upload
    $result = $googleDrive->uploadToFolder($file, $folderId);
    
    if ($result['success']) {
        Document::create([
            'title' => $request->title,
            'google_drive_file_id' => $result['file_id'],
            'file_name' => $result['file_name'],
        ]);
        
        return redirect()->back()->with('success', 'Upload realizado!');
    }
    
    return redirect()->back()->with('error', $result['error']);
}

3. Criar Estrutura de Curso

public function store(Request $request, GoogleDriveService $googleDrive)
{
    // Criar estrutura no Google Drive
    $result = $googleDrive->createCourseStructure(
        $request->title,
        $request->category->slug ?? null
    );
    
    if ($result['success']) {
        $course = Course::create([
            'title' => $request->title,
            'google_drive_folder_id' => $result['course_folder_id'],
        ]);
        
        // Upload de capa
        if ($request->hasFile('cover_image')) {
            $coverResult = $googleDrive->uploadToFolder(
                $request->file('cover_image'),
                $result['attachments_folder_id'],
                'capa.jpg'
            );
            
            $course->update([
                'cover_image_drive_id' => $coverResult['file_id'],
            ]);
        }
        
        return redirect()->route('admin.courses.index')
            ->with('success', 'Curso criado!');
    }
}

🔒 Segurança

Boas Práticas

  1. Nunca expor credenciais

    • Client ID e Secret devem estar no .env
    • Adicionar .env no .gitignore
  2. Refresh Token

    • Armazenar com segurança
    • Nunca compartilhar publicamente
    • Renovar periodicamente
  3. Permissões de Arquivos

    • Por padrão, arquivos são privados
    • Usar proxy autenticado para streaming
    • Validar acesso do usuário antes de servir arquivo
  4. Validação de Upload

    $request->validate([
        'file' => 'required|file|max:102400|mimes:pdf,doc,docx',
    ]);
  5. Rate Limiting

    • Implementar rate limit nas rotas de upload
    • Limitar tamanho de arquivos

🐛 Troubleshooting

Erro: "Refresh token not found"

Solução: Execute o fluxo OAuth novamente para obter novo refresh token.

// Acessar rota de conexão
https://seu-dominio.com/admin/settings/google-drive/connect

Erro: "Invalid credentials"

Solução: Verificar se Client ID e Secret estão corretos no .env

php artisan config:clear
php artisan cache:clear

Erro: "Folder not found"

Solução: Verificar se o ID da pasta está correto

// Listar pastas disponíveis
$folders = $googleDrive->listFolders();

Erro: "Token has been expired or revoked"

Solução: Refresh token expirou, refazer autenticação OAuth

Upload falha para arquivos grandes

Solução: Aumentar limites no php.ini

upload_max_filesize = 512M
post_max_size = 512M
max_execution_time = 300
memory_limit = 512M

Erro: "Insufficient permissions"

Solução: Verificar escopos OAuth

// Adicionar escopo completo
$client->addScope(GoogleDrive::DRIVE);

📚 Referências


📝 Checklist de Implementação

  • Criar projeto no Google Cloud Console
  • Habilitar Google Drive API
  • Configurar tela de consentimento OAuth
  • Criar credenciais OAuth 2.0
  • Instalar dependências via Composer
  • Configurar variáveis de ambiente (.env)
  • Criar GoogleDriveServiceProvider
  • Registrar Service Provider
  • Criar GoogleDriveService
  • Implementar rotas de autenticação
  • Implementar controller de autenticação
  • Testar fluxo OAuth
  • Implementar upload de arquivos
  • Implementar download/streaming
  • Implementar gerenciamento de pastas
  • Adicionar validações de segurança
  • Testar em produção

📚 Google Drive API - Exemplos Práticos

Autor: Dante Testa
Data: 24/11/2025


📋 Índice

  1. Controller Completo de Upload
  2. Sistema de Cursos
  3. Sistema de Downloads
  4. Proxy para Streaming
  5. Migrations
  6. Rotas Completas

🎯 Controller Completo de Upload

CourseController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Course;
use App\Models\CourseCategory;
use App\Services\GoogleDriveService;
use Illuminate\Http\Request;

class CourseController extends Controller
{
    /**
     * Criar novo curso com estrutura no Google Drive
     */
    public function store(Request $request, GoogleDriveService $googleDrive)
    {
        $validated = $request->validate([
            'category_id' => 'nullable|exists:course_categories,id',
            'title' => 'required|string|max:255',
            'description' => 'required|string',
            'cover_image' => 'nullable|image|max:10240', // 10MB
            'presentation_video' => 'nullable|file|mimes:mp4,mov|max:512000', // 500MB
            'is_free' => 'boolean',
        ]);
        
        try {
            // Buscar categoria se informada
            $category = $validated['category_id'] 
                ? CourseCategory::find($validated['category_id']) 
                : null;
            
            // Criar estrutura no Google Drive
            $driveResult = $googleDrive->createCourseStructure(
                $validated['title'],
                $category?->slug
            );
            
            if (!$driveResult['success']) {
                return redirect()->back()
                    ->with('error', 'Erro ao criar estrutura: ' . $driveResult['error'])
                    ->withInput();
            }
            
            // Criar curso no banco
            $course = Course::create([
                'category_id' => $validated['category_id'],
                'title' => $validated['title'],
                'slug' => \Str::slug($validated['title']),
                'description' => $validated['description'],
                'is_free' => $validated['is_free'] ?? false,
                'google_drive_folder_id' => $driveResult['course_folder_id'],
                'attachments_folder_id' => $driveResult['attachments_folder_id'],
            ]);
            
            // Upload de capa
            if ($request->hasFile('cover_image')) {
                $coverResult = $googleDrive->uploadToFolder(
                    $request->file('cover_image'),
                    $driveResult['attachments_folder_id'],
                    'capa.jpg'
                );
                
                if ($coverResult['success']) {
                    $course->update([
                        'cover_image_drive_id' => $coverResult['file_id'],
                        'cover_image_url' => $coverResult['file_url'],
                    ]);
                }
            }
            
            // Upload de vídeo de apresentação
            if ($request->hasFile('presentation_video')) {
                $videoResult = $googleDrive->uploadToFolder(
                    $request->file('presentation_video'),
                    $driveResult['attachments_folder_id'],
                    'apresentacao.mp4'
                );
                
                if ($videoResult['success']) {
                    $course->update([
                        'presentation_video_drive_id' => $videoResult['file_id'],
                        'presentation_video_url' => $videoResult['file_url'],
                    ]);
                }
            }
            
            return redirect()->route('admin.courses.index')
                ->with('success', 'Curso criado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao criar curso', [
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao criar curso: ' . $e->getMessage())
                ->withInput();
        }
    }
    
    /**
     * Atualizar curso
     */
    public function update(Request $request, Course $course, GoogleDriveService $googleDrive)
    {
        $validated = $request->validate([
            'category_id' => 'nullable|exists:course_categories,id',
            'title' => 'required|string|max:255',
            'description' => 'required|string',
            'cover_image' => 'nullable|image|max:10240',
            'presentation_video' => 'nullable|file|mimes:mp4,mov|max:512000',
            'is_free' => 'boolean',
        ]);
        
        try {
            // Se mudou de categoria, mover pasta no Google Drive
            if ($validated['category_id'] != $course->category_id) {
                $newCategory = $validated['category_id'] 
                    ? CourseCategory::find($validated['category_id']) 
                    : null;
                
                $googleDrive->moveCourseFolderToCategory(
                    $course->google_drive_folder_id,
                    $newCategory
                );
            }
            
            // Se mudou o título, renomear pasta no Google Drive
            if ($validated['title'] != $course->title) {
                $googleDrive->renameCourseFolder(
                    $course->google_drive_folder_id,
                    $validated['title']
                );
            }
            
            // Atualizar curso
            $course->update([
                'category_id' => $validated['category_id'],
                'title' => $validated['title'],
                'slug' => \Str::slug($validated['title']),
                'description' => $validated['description'],
                'is_free' => $validated['is_free'] ?? false,
            ]);
            
            // Upload de nova capa
            if ($request->hasFile('cover_image')) {
                // Deletar capa antiga
                if ($course->cover_image_drive_id) {
                    $googleDrive->deleteFile($course->cover_image_drive_id);
                }
                
                // Upload nova capa
                $coverResult = $googleDrive->uploadToFolder(
                    $request->file('cover_image'),
                    $course->attachments_folder_id,
                    'capa.jpg'
                );
                
                if ($coverResult['success']) {
                    $course->update([
                        'cover_image_drive_id' => $coverResult['file_id'],
                        'cover_image_url' => $coverResult['file_url'],
                    ]);
                }
            }
            
            return redirect()->route('admin.courses.index')
                ->with('success', 'Curso atualizado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao atualizar curso', [
                'course_id' => $course->id,
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao atualizar curso: ' . $e->getMessage())
                ->withInput();
        }
    }
    
    /**
     * Deletar curso
     */
    public function destroy(Course $course, GoogleDriveService $googleDrive)
    {
        try {
            // Deletar pasta do Google Drive
            if ($course->google_drive_folder_id) {
                $googleDrive->deleteCourseFolder($course->google_drive_folder_id);
            }
            
            // Deletar curso do banco
            $course->delete();
            
            return redirect()->route('admin.courses.index')
                ->with('success', 'Curso deletado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao deletar curso', [
                'course_id' => $course->id,
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao deletar curso: ' . $e->getMessage());
        }
    }
}

📚 Sistema de Cursos

ModuleController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Module;
use App\Models\Course;
use App\Services\GoogleDriveService;
use Illuminate\Http\Request;

class ModuleController extends Controller
{
    public function store(Request $request, GoogleDriveService $googleDrive)
    {
        $validated = $request->validate([
            'course_id' => 'required|exists:courses,id',
            'title' => 'required|string|max:255',
            'order' => 'required|integer|min:1',
            'is_free' => 'boolean',
        ]);
        
        try {
            $course = Course::findOrFail($validated['course_id']);
            
            // Nome da pasta: "Módulo X - Título"
            $folderName = "Modulo {$validated['order']} - {$validated['title']}";
            
            // Criar pasta no Google Drive
            $driveResult = $googleDrive->createModuleFolder(
                $folderName,
                $course->google_drive_folder_id
            );
            
            if (!$driveResult['success']) {
                return redirect()->back()
                    ->with('error', 'Erro ao criar pasta: ' . $driveResult['error'])
                    ->withInput();
            }
            
            // Criar módulo no banco
            $module = Module::create([
                'course_id' => $validated['course_id'],
                'title' => $validated['title'],
                'order' => $validated['order'],
                'is_free' => $validated['is_free'] ?? false,
                'google_drive_folder_id' => $driveResult['module_folder_id'],
                'attachments_folder_id' => $driveResult['attachments_folder_id'],
            ]);
            
            return redirect()->route('admin.modules.index')
                ->with('success', 'Módulo criado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao criar módulo', [
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao criar módulo: ' . $e->getMessage())
                ->withInput();
        }
    }
}

LessonController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Lesson;
use App\Models\Module;
use App\Services\GoogleDriveService;
use Illuminate\Http\Request;

class LessonController extends Controller
{
    public function store(Request $request, GoogleDriveService $googleDrive)
    {
        $validated = $request->validate([
            'module_id' => 'required|exists:modules,id',
            'title' => 'required|string|max:255',
            'order' => 'required|integer|min:1',
            'video_file' => 'required|file|mimes:mp4,mov|max:512000',
            'is_free_preview' => 'boolean',
            'attachments.*' => 'nullable|file|max:51200', // 50MB cada
        ]);
        
        try {
            $module = Module::findOrFail($validated['module_id']);
            
            // Nome da pasta: "Aula X - Título"
            $folderName = "Aula {$validated['order']} - {$validated['title']}";
            
            // Criar pasta da aula
            $driveResult = $googleDrive->createLessonFolder(
                $folderName,
                $module->google_drive_folder_id
            );
            
            if (!$driveResult['success']) {
                return redirect()->back()
                    ->with('error', 'Erro ao criar pasta: ' . $driveResult['error'])
                    ->withInput();
            }
            
            // Upload do vídeo
            $videoResult = $googleDrive->uploadToFolder(
                $request->file('video_file'),
                $driveResult['lesson_folder_id'],
                'video.mp4'
            );
            
            if (!$videoResult['success']) {
                return redirect()->back()
                    ->with('error', 'Erro ao fazer upload do vídeo: ' . $videoResult['error'])
                    ->withInput();
            }
            
            // Criar aula no banco
            $lesson = Lesson::create([
                'module_id' => $validated['module_id'],
                'title' => $validated['title'],
                'order' => $validated['order'],
                'is_free_preview' => $validated['is_free_preview'] ?? false,
                'google_drive_folder_id' => $driveResult['lesson_folder_id'],
                'video_drive_id' => $videoResult['file_id'],
                'video_url' => $videoResult['file_url'],
            ]);
            
            // Upload de anexos
            if ($request->hasFile('attachments')) {
                foreach ($request->file('attachments') as $attachment) {
                    $attachmentResult = $googleDrive->uploadToFolder(
                        $attachment,
                        $driveResult['lesson_folder_id'],
                        $attachment->getClientOriginalName()
                    );
                    
                    if ($attachmentResult['success']) {
                        $lesson->attachments()->create([
                            'title' => $attachment->getClientOriginalName(),
                            'file_drive_id' => $attachmentResult['file_id'],
                            'file_url' => $attachmentResult['file_url'],
                            'file_type' => $attachment->getClientMimeType(),
                            'file_size' => $attachment->getSize(),
                        ]);
                    }
                }
            }
            
            return redirect()->route('admin.lessons.index')
                ->with('success', 'Aula criada com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao criar aula', [
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao criar aula: ' . $e->getMessage())
                ->withInput();
        }
    }
}

📥 Sistema de Downloads

DownloadController.php

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Download;
use App\Models\DownloadCategory;
use App\Services\GoogleDriveService;
use Illuminate\Http\Request;

class DownloadController extends Controller
{
    public function store(Request $request, GoogleDriveService $googleDrive)
    {
        $validated = $request->validate([
            'category_id' => 'nullable|exists:download_categories,id',
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'file' => 'required|file|max:512000', // 500MB
        ]);
        
        try {
            $category = $validated['category_id'] 
                ? DownloadCategory::find($validated['category_id']) 
                : null;
            
            // Upload organizado: downloads/categoria/nome-download/arquivo.ext
            $uploadResult = $googleDrive->uploadToOrganizedFolder(
                $request->file('file'),
                $validated['title'],
                $category?->slug
            );
            
            if (!$uploadResult['success']) {
                return redirect()->back()
                    ->with('error', 'Erro ao fazer upload: ' . $uploadResult['error'])
                    ->withInput();
            }
            
            // Criar download no banco
            $download = Download::create([
                'category_id' => $validated['category_id'],
                'title' => $validated['title'],
                'slug' => \Str::slug($validated['title']),
                'description' => $validated['description'],
                'file_drive_id' => $uploadResult['file_id'],
                'file_name' => $uploadResult['file_name'],
                'file_url' => $uploadResult['web_view_link'],
                'download_url' => $uploadResult['download_link'],
                'folder_path' => $uploadResult['folder_path'],
            ]);
            
            return redirect()->route('admin.downloads.index')
                ->with('success', 'Download criado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao criar download', [
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao criar download: ' . $e->getMessage())
                ->withInput();
        }
    }
    
    public function destroy(Download $download, GoogleDriveService $googleDrive)
    {
        try {
            // Deletar pasta do Google Drive
            $category = $download->category;
            $googleDrive->deleteDownloadFolder(
                $download->title,
                $category?->slug
            );
            
            // Deletar do banco
            $download->delete();
            
            return redirect()->route('admin.downloads.index')
                ->with('success', 'Download deletado com sucesso!');
                
        } catch (\Exception $e) {
            \Log::error('Erro ao deletar download', [
                'download_id' => $download->id,
                'error' => $e->getMessage()
            ]);
            
            return redirect()->back()
                ->with('error', 'Erro ao deletar download: ' . $e->getMessage());
        }
    }
}

🎬 Proxy para Streaming

GoogleDriveProxyController.php

<?php

namespace App\Http\Controllers;

use App\Services\GoogleDriveService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Response;

class GoogleDriveProxyController extends Controller
{
    /**
     * Fazer streaming de vídeo do Google Drive
     * Rota: /drive/stream/{fileId}
     */
    public function stream($fileId, GoogleDriveService $googleDrive)
    {
        // Verificar autenticação
        if (!auth()->check()) {
            abort(403, 'Acesso negado');
        }
        
        // Fazer streaming via API
        $result = $googleDrive->streamFile($fileId);
        
        if (!$result['success']) {
            abort(404, 'Arquivo não encontrado');
        }
        
        return response($result['content'])
            ->header('Content-Type', $result['mime_type'])
            ->header('Content-Length', $result['size'])
            ->header('Accept-Ranges', 'bytes')
            ->header('Cache-Control', 'no-cache, no-store, must-revalidate');
    }
    
    /**
     * Fazer download de arquivo do Google Drive
     * Rota: /drive/download/{fileId}
     */
    public function download($fileId, GoogleDriveService $googleDrive)
    {
        // Verificar autenticação
        if (!auth()->check()) {
            abort(403, 'Acesso negado');
        }
        
        // Fazer download via API
        $result = $googleDrive->downloadFile($fileId);
        
        if (!$result['success']) {
            abort(404, 'Arquivo não encontrado');
        }
        
        return response($result['content'])
            ->header('Content-Type', $result['mimeType'])
            ->header('Content-Disposition', 'attachment; filename="' . $result['filename'] . '"')
            ->header('Content-Length', $result['size']);
    }
}

🗄️ Migrations

Courses Table

Schema::create('courses', function (Blueprint $table) {
    $table->id();
    $table->foreignId('category_id')->nullable()->constrained('course_categories')->nullOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('description');
    $table->boolean('is_free')->default(false);
    
    // Google Drive
    $table->string('google_drive_folder_id')->nullable();
    $table->string('attachments_folder_id')->nullable();
    $table->string('cover_image_drive_id')->nullable();
    $table->string('cover_image_url')->nullable();
    $table->string('presentation_video_drive_id')->nullable();
    $table->string('presentation_video_url')->nullable();
    
    $table->timestamps();
});

Modules Table

Schema::create('modules', function (Blueprint $table) {
    $table->id();
    $table->foreignId('course_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->integer('order')->default(1);
    $table->boolean('is_free')->default(false);
    
    // Google Drive
    $table->string('google_drive_folder_id')->nullable();
    $table->string('attachments_folder_id')->nullable();
    
    $table->timestamps();
});

Lessons Table

Schema::create('lessons', function (Blueprint $table) {
    $table->id();
    $table->foreignId('module_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->integer('order')->default(1);
    $table->boolean('is_free_preview')->default(false);
    
    // Google Drive
    $table->string('google_drive_folder_id')->nullable();
    $table->string('video_drive_id')->nullable();
    $table->string('video_url')->nullable();
    
    $table->timestamps();
});

Downloads Table

Schema::create('downloads', function (Blueprint $table) {
    $table->id();
    $table->foreignId('category_id')->nullable()->constrained('download_categories')->nullOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('description')->nullable();
    
    // Google Drive
    $table->string('file_drive_id')->nullable();
    $table->string('file_name')->nullable();
    $table->string('file_url')->nullable();
    $table->string('download_url')->nullable();
    $table->string('folder_path')->nullable();
    
    $table->timestamps();
});

🛣️ Rotas Completas

routes/admin.php

use App\Http\Controllers\Admin\{
    CourseController,
    ModuleController,
    LessonController,
    DownloadController,
    GoogleDriveSettingsController
};

Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    
    // Cursos
    Route::resource('courses', CourseController::class);
    
    // Módulos
    Route::resource('modules', ModuleController::class);
    
    // Aulas
    Route::resource('lessons', LessonController::class);
    
    // Downloads
    Route::resource('downloads', DownloadController::class);
    
    // Google Drive Settings
    Route::prefix('settings/google-drive')->name('settings.google-drive.')->group(function () {
        Route::get('/', [GoogleDriveSettingsController::class, 'index'])->name('index');
        Route::get('/connect', [GoogleDriveSettingsController::class, 'connect'])->name('connect');
        Route::get('/callback', [GoogleDriveSettingsController::class, 'callback'])->name('callback');
        Route::post('/disconnect', [GoogleDriveSettingsController::class, 'disconnect'])->name('disconnect');
    });
});

routes/web.php

use App\Http\Controllers\GoogleDriveProxyController;

// Proxy para streaming/download (autenticado)
Route::middleware('auth')->group(function () {
    Route::get('/drive/stream/{fileId}', [GoogleDriveProxyController::class, 'stream'])->name('drive.stream');
    Route::get('/drive/download/{fileId}', [GoogleDriveProxyController::class, 'download'])->name('drive.download');
});

Desenvolvido por: Dante Testa
Data: 24/11/2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment