mirror of
https://github.com/Nezumi-2711/next-gdrive-index.git
synced 2026-09-23 04:10:03 +00:00
Remove .env
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import apiConfig from "@config/api.config";
|
||||
import { google, drive_v3 } from "googleapis";
|
||||
|
||||
class DriveClient {
|
||||
private instance: drive_v3.Drive;
|
||||
|
||||
constructor() {
|
||||
const oauth2Client = new google.auth.OAuth2(
|
||||
apiConfig.client_id,
|
||||
process.env.CLIENT_SECRET,
|
||||
apiConfig.redirect_uri,
|
||||
);
|
||||
oauth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN });
|
||||
this.instance = google.drive({ version: "v3", auth: oauth2Client });
|
||||
}
|
||||
|
||||
getInstance() {
|
||||
if (!this.instance) {
|
||||
const oauth2Client = new google.auth.OAuth2(
|
||||
apiConfig.client_id,
|
||||
process.env.CLIENT_SECRET,
|
||||
apiConfig.redirect_uri,
|
||||
);
|
||||
oauth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN });
|
||||
this.instance = google.drive({ version: "v3", auth: oauth2Client });
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
}
|
||||
|
||||
const drive = new DriveClient();
|
||||
|
||||
export default drive.getInstance();
|
||||
@@ -0,0 +1,70 @@
|
||||
import drive from "@utils/driveClient";
|
||||
import config from "@config/site.config";
|
||||
|
||||
export function buildQuery({
|
||||
id,
|
||||
extraQuery,
|
||||
globalSearch = false,
|
||||
}: {
|
||||
id?: string;
|
||||
extraQuery?: string[];
|
||||
globalSearch?: boolean;
|
||||
}) {
|
||||
const query = [
|
||||
"name != '.password'",
|
||||
"'me' in owners",
|
||||
"trashed = false",
|
||||
// "mimeType != 'application/vnd.google-apps.shortcut'",
|
||||
];
|
||||
|
||||
if (id && !globalSearch) {
|
||||
query.unshift(`'${id}' in parents`);
|
||||
}
|
||||
if (!id && !globalSearch) {
|
||||
query.unshift(`'${config.files.rootFolder}' in parents`);
|
||||
}
|
||||
|
||||
if (extraQuery) {
|
||||
query.unshift(...extraQuery);
|
||||
}
|
||||
|
||||
return query.join(" and ");
|
||||
}
|
||||
|
||||
export async function checkProtected(id: string) {
|
||||
try {
|
||||
const files = await drive.files.list({
|
||||
q: id ? buildQuery({ id }) : buildQuery({}),
|
||||
fields: "files(id)",
|
||||
});
|
||||
|
||||
return {
|
||||
protected: files.data.files?.length,
|
||||
id: files.data.files?.[0].id || null,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
protected: false,
|
||||
id: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export async function validateFolderPassword(
|
||||
passwordFileId: string,
|
||||
password: string,
|
||||
) {
|
||||
try {
|
||||
const folderPassword = await drive.files.get(
|
||||
{
|
||||
fileId: passwordFileId,
|
||||
alt: "media",
|
||||
},
|
||||
{ responseType: "text" },
|
||||
);
|
||||
|
||||
return folderPassword.data === password;
|
||||
} catch (error: any) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
export function generateRandomEncryptionKey(): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
crypto.randomBytes(32, (err, buffer) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(buffer.toString("hex"));
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// Get locale from browser
|
||||
export function getLocale() {
|
||||
if (typeof window === "undefined") return "en-US";
|
||||
if (window.navigator.languages) return window.navigator.languages[0];
|
||||
return window.navigator.language;
|
||||
}
|
||||
|
||||
export function formatDuration(milliseconds: string | number) {
|
||||
let ms: number = milliseconds as number;
|
||||
if (typeof milliseconds === "string") {
|
||||
ms = parseInt(milliseconds);
|
||||
}
|
||||
const totalSeconds = Math.floor(ms / 1000);
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
const hourString = hours > 0 ? `${hours}:` : "";
|
||||
const minuteString =
|
||||
minutes > 0 ? `${String(minutes).padStart(2, "0")}:` : "00:";
|
||||
const secondString = seconds > 0 ? String(seconds).padStart(2, "0") : "00";
|
||||
|
||||
return `${hourString}${minuteString}${secondString}`;
|
||||
}
|
||||
|
||||
export function formatBytes(bytes: number | string) {
|
||||
let b: number = typeof bytes === "string" ? parseInt(bytes) : bytes;
|
||||
if (b === 0) return "0 Bytes";
|
||||
|
||||
const k = 1024;
|
||||
const dm = 2;
|
||||
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
const i = Math.floor(Math.log(b) / Math.log(k));
|
||||
|
||||
return parseFloat((b / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
||||
}
|
||||
|
||||
export function formatDate(date: Date, locale = getLocale()) {
|
||||
return new Intl.DateTimeFormat(locale, {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
hour12: false,
|
||||
}).format(date);
|
||||
}
|
||||
|
||||
type RelativeTimeFormatUnit =
|
||||
| "year"
|
||||
| "quarter"
|
||||
| "month"
|
||||
| "week"
|
||||
| "day"
|
||||
| "hour"
|
||||
| "minute"
|
||||
| "second";
|
||||
export function formatRelativeDate(
|
||||
date: Date,
|
||||
unit: RelativeTimeFormatUnit = "day",
|
||||
locale = getLocale(),
|
||||
) {
|
||||
const currentDate = Date.now();
|
||||
let diff = (date.getTime() - currentDate) / (1000 * 60 * 60 * 24);
|
||||
|
||||
// if negative, round up, else round down
|
||||
if (diff < 0) {
|
||||
diff = Math.ceil(diff);
|
||||
} else {
|
||||
diff = Math.floor(diff);
|
||||
}
|
||||
|
||||
return new Intl.RelativeTimeFormat(locale, {
|
||||
localeMatcher: "best fit",
|
||||
numeric: "auto",
|
||||
style: "long",
|
||||
}).format(diff, unit);
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
import mime from "mime-types";
|
||||
import { IconType } from "react-icons";
|
||||
import {
|
||||
BsBoxFill,
|
||||
BsDatabaseFill,
|
||||
BsFileEarmarkBinaryFill,
|
||||
BsFileEarmarkBreakFill,
|
||||
BsFileEarmarkCodeFill,
|
||||
BsFileEarmarkFill,
|
||||
BsFileEarmarkFontFill,
|
||||
BsFileEarmarkImageFill,
|
||||
BsFileEarmarkMusicFill,
|
||||
BsFileEarmarkPdfFill,
|
||||
BsFileEarmarkPlayFill,
|
||||
BsFileEarmarkRichtextFill,
|
||||
BsFileEarmarkRuledFill,
|
||||
BsFileEarmarkSlidesFill,
|
||||
BsFileEarmarkSpreadsheetFill,
|
||||
BsFileEarmarkTextFill,
|
||||
BsFileEarmarkWordFill,
|
||||
BsFileEarmarkZipFill,
|
||||
BsFillDatabaseFill,
|
||||
BsFolderFill,
|
||||
} from "react-icons/bs";
|
||||
import ModelPreview from "@components/FilePreview/ModelPreview";
|
||||
import AudioPreview from "@components/FilePreview/AudioPreview";
|
||||
import DefaultPreview from "@components/FilePreview/DefaultPreview";
|
||||
import MarkdownPreview from "@components/FilePreview/MarkdownPreview";
|
||||
import OfficePreview from "@components/FilePreview/OfficePreview";
|
||||
import PDFPreview from "@components/FilePreview/PDFPreview";
|
||||
import ImagePreview from "@components/FilePreview/ImagePreview";
|
||||
import CodePreview from "@components/FilePreview/CodePreview";
|
||||
import TextPreview from "@components/FilePreview/TextPreview";
|
||||
import VideoPreview from "@components/FilePreview/VideoPreview";
|
||||
|
||||
export default function findMimeType(extension: string): string {
|
||||
return mime.lookup(extension) || "application/octet-stream";
|
||||
}
|
||||
|
||||
const type = {
|
||||
"3d": "3d", // model preview
|
||||
"audio": "audio", // audio preview
|
||||
"archive": "archive", // default preview
|
||||
"rich_text": "markdown", // markdown preview
|
||||
"officeWord": "officeWord", // office preview
|
||||
"officeExcel": "officeExcel", // office preview
|
||||
"officePowerPoint": "officePowerPoint", // office preview
|
||||
"pdf": "pdf", // pdf preview
|
||||
"database": "database", // default preview
|
||||
"image": "image", // image preview
|
||||
"code": "code", // code preview
|
||||
"text": "text", // text preview
|
||||
"video": "video", // video preview
|
||||
"font": "font", // default preview
|
||||
"default": "default", // default preview
|
||||
"binary": "binary", // default preview
|
||||
};
|
||||
|
||||
const iconsForType: { [key: string]: IconType } = {
|
||||
"3d": BsBoxFill,
|
||||
"archive": BsFileEarmarkZipFill,
|
||||
"audio": BsFileEarmarkMusicFill,
|
||||
"rich_text": BsFileEarmarkRichtextFill,
|
||||
"officeWord": BsFileEarmarkWordFill,
|
||||
"officeExcel": BsFileEarmarkSpreadsheetFill,
|
||||
"officePowerPoint": BsFileEarmarkSlidesFill,
|
||||
"pdf": BsFileEarmarkPdfFill,
|
||||
"database": BsDatabaseFill,
|
||||
"image": BsFileEarmarkImageFill,
|
||||
"code": BsFileEarmarkCodeFill,
|
||||
"text": BsFileEarmarkTextFill,
|
||||
"video": BsFileEarmarkPlayFill,
|
||||
"font": BsFileEarmarkFontFill,
|
||||
"default": BsFileEarmarkFill,
|
||||
"binary": BsFileEarmarkBinaryFill,
|
||||
};
|
||||
|
||||
const overlapVideo = ["ts", "ogg"];
|
||||
|
||||
const extToTypeMap: { [key: string]: string } = {
|
||||
// 3D models
|
||||
"fbx": type["3d"],
|
||||
"obj": type["3d"],
|
||||
"stl": type["3d"],
|
||||
"gltf": type["3d"],
|
||||
"glb": type["3d"],
|
||||
|
||||
// Audio
|
||||
"aac": type.audio,
|
||||
"flac": type.audio,
|
||||
"m4a": type.audio,
|
||||
"mp3": type.audio,
|
||||
"ogg": type.audio, // Check for audio or video
|
||||
"opus": type.audio,
|
||||
"wav": type.audio,
|
||||
|
||||
// Archives
|
||||
"7z": type.default,
|
||||
"bz2": type.default,
|
||||
"gz": type.default,
|
||||
"rar": type.default,
|
||||
"tar": type.default,
|
||||
"zip": type.default,
|
||||
|
||||
// Rich text
|
||||
"md": type.rich_text,
|
||||
|
||||
// Office
|
||||
"doc": type.officeWord,
|
||||
"docx": type.officeWord,
|
||||
"odt": type.officeWord,
|
||||
"xls": type.officeExcel,
|
||||
"xlsx": type.officeExcel,
|
||||
"ods": type.officeExcel,
|
||||
"ppt": type.officePowerPoint,
|
||||
"pptx": type.officePowerPoint,
|
||||
"odp": type.officePowerPoint,
|
||||
|
||||
// PDF
|
||||
"pdf": type.pdf,
|
||||
|
||||
// Database
|
||||
"db": type.database,
|
||||
"dbf": type.database,
|
||||
"mdb": type.database,
|
||||
"pdb": type.database,
|
||||
"sql": type.database,
|
||||
"csv": type.database,
|
||||
"tsv": type.database,
|
||||
|
||||
// Images
|
||||
"bmp": type.image,
|
||||
"gif": type.image,
|
||||
"jpg": type.image,
|
||||
"jpeg": type.image,
|
||||
"png": type.image,
|
||||
"svg": type.image,
|
||||
"webp": type.image,
|
||||
"apng": type.image, //Should be able to preview according to MDN
|
||||
"avif": type.image,
|
||||
"ico": type.image,
|
||||
|
||||
// Code
|
||||
|
||||
"c": type.code,
|
||||
"cpp": type.code,
|
||||
"cs": type.code,
|
||||
"css": type.code,
|
||||
"go": type.code,
|
||||
"h": type.code,
|
||||
"html": type.code,
|
||||
"ini": type.code,
|
||||
"java": type.code,
|
||||
"js": type.code,
|
||||
"json": type.code,
|
||||
"jsx": type.code,
|
||||
"php": type.code,
|
||||
"py": type.code,
|
||||
"rb": type.code,
|
||||
"rs": type.code,
|
||||
"rust": type.code,
|
||||
"sass": type.code,
|
||||
"scss": type.code,
|
||||
"sh": type.code,
|
||||
"swift": type.code,
|
||||
"ts": type.code, // Check for video also
|
||||
"tsx": type.code,
|
||||
"xml": type.code,
|
||||
"yaml": type.code,
|
||||
|
||||
// Text
|
||||
"txt": type.text,
|
||||
|
||||
// Video
|
||||
"mp4": type.video,
|
||||
"webm": type.video,
|
||||
"3gp": type.video,
|
||||
"mpeg": type.video,
|
||||
"mov": type.video,
|
||||
"mkv": type.video,
|
||||
|
||||
// Fonts
|
||||
"woff": type.font,
|
||||
"woff2": type.font,
|
||||
"ttf": type.font,
|
||||
"otf": type.font,
|
||||
|
||||
// Binary
|
||||
"dat": type.binary,
|
||||
"bin": type.binary,
|
||||
"exe": type.binary,
|
||||
"dll": type.binary,
|
||||
"msi": type.binary,
|
||||
};
|
||||
|
||||
export function getFilePreview(extension: string) {
|
||||
if (overlapVideo.includes(extension)) {
|
||||
const isVideo = findMimeType(extension).startsWith("video");
|
||||
if (isVideo) {
|
||||
return VideoPreview;
|
||||
}
|
||||
}
|
||||
const category = extToTypeMap[extension] || type.default;
|
||||
switch (category) {
|
||||
case type["3d"]:
|
||||
return ModelPreview;
|
||||
case type.audio:
|
||||
return AudioPreview;
|
||||
case type.rich_text:
|
||||
return MarkdownPreview;
|
||||
case type.officeWord:
|
||||
case type.officeExcel:
|
||||
case type.officePowerPoint:
|
||||
return OfficePreview;
|
||||
case type.pdf:
|
||||
return PDFPreview;
|
||||
case type.image:
|
||||
return ImagePreview;
|
||||
case type.code:
|
||||
return CodePreview;
|
||||
case type.text:
|
||||
return TextPreview;
|
||||
case type.video:
|
||||
return VideoPreview;
|
||||
default:
|
||||
return DefaultPreview;
|
||||
}
|
||||
}
|
||||
|
||||
export function getFileIcon(extension: string): IconType {
|
||||
if (overlapVideo.includes(extension)) {
|
||||
const isVideo = findMimeType(extension).startsWith("video");
|
||||
if (isVideo) {
|
||||
return iconsForType["video"];
|
||||
}
|
||||
}
|
||||
const category = extToTypeMap[extension] || type.default;
|
||||
return iconsForType[category];
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import axios from "axios";
|
||||
import {FilesResponse} from "@/types/googleapis";
|
||||
|
||||
const fetcher = async <T>(url: string) =>
|
||||
axios.get<T>(url).then((res) => res.data);
|
||||
|
||||
function getNextKey(
|
||||
pageIndex: number,
|
||||
previousPageData: FilesResponse,
|
||||
): string | null {
|
||||
if (previousPageData && !previousPageData.nextPageToken) {
|
||||
return null;
|
||||
}
|
||||
const pageToken = previousPageData ? previousPageData.nextPageToken : "";
|
||||
return `/api/files?pageToken=${pageToken}`;
|
||||
}
|
||||
|
||||
export function buildNextKey(apiURL:string) {
|
||||
return (pageIndex: number, previousPageData: FilesResponse) => {
|
||||
if (previousPageData && !previousPageData.nextPageToken) {
|
||||
return null;
|
||||
}
|
||||
const pageToken = previousPageData ? previousPageData.nextPageToken : "";
|
||||
return `${apiURL}?pageToken=${pageToken}`;
|
||||
}
|
||||
}
|
||||
|
||||
export default fetcher;
|
||||
Reference in New Issue
Block a user