Skip to content

Commit

Permalink
feat: do stuff on request by the parent application
Browse files Browse the repository at this point in the history
- Move dev.ts to src/dev.ts
- Update .npmignore and .gitignore to ignore data folder and TypeScript files
- Remove unused config/config.ts file
- Rename googleDriveService.ts to google-drive.ts
- Update import paths in index.ts and utils/index.ts
  • Loading branch information
totallynotdavid committed Oct 1, 2024
1 parent 3bf189b commit e8b7ba0
Show file tree
Hide file tree
Showing 16 changed files with 309 additions and 244 deletions.
2 changes: 1 addition & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ updates:
- package-ecosystem: 'npm'
directory: '/' # package.json is in the root directory
schedule:
interval: 'daily'
interval: 'daily'
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ jobs:

- uses: github/codeql-action/autobuild@v3

- uses: github/codeql-action/analyze@v3
- uses: github/codeql-action/analyze@v3
3 changes: 2 additions & 1 deletion .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
.changeset
node_modules
dist
src/dev.ts
data

*.ts
.gitignore
.prettierignore
eslint.config.js
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ module.exports = [
},
},
},
];
];
87 changes: 47 additions & 40 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
{
"name": "gdrivevault",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"dev": "ts-node -r tsconfig-paths/register src/dev.ts",
"clean": "shx rm -rf dist node_modules pnpm-lock.yaml",
"lint": "eslint --fix --color src/**/*.ts",
"prettier": "prettier --write src/**/*.ts"
},
"keywords": [],
"author": "David Duran <contacto@altmails.com>",
"license": "MIT",
"dependencies": {
"@google-cloud/local-auth": "^3.0.1",
"dotenv": "^16.4.5",
"envalid": "^8.0.0",
"google-auth-library": "^9.14.1",
"googleapis": "^144.0.0",
"node-fetch": "^2.6.6",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"winston": "^3.14.2"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@types/eslint__js": "^8.42.3",
"@types/node": "^22.5.5",
"@types/node-fetch": "^2.6.11",
"eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"prettier": "^3.3.3",
"shx": "^0.3.4",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.6.0",
"typescript-eslint": "^8.8.0"
}
"name": "gdrivevault",
"version": "1.0.0",
"description": "Searching and downloading files from Google Drive folders should be easy",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc",
"dev": "ts-node -r tsconfig-paths/register src/dev.ts",
"clean": "shx rm -rf dist node_modules pnpm-lock.yaml",
"lint": "eslint --fix --color src/**/*.ts",
"prettier": "prettier --write src/**/*.ts"
},
"keywords": [
"google-drive",
"file-manager"
],
"author": "David Duran <contacto@altmails.com>",
"license": "MIT",
"dependencies": {
"@google-cloud/local-auth": "^3.0.1",
"dotenv": "^16.4.5",
"envalid": "^8.0.0",
"google-auth-library": "^9.14.1",
"googleapis": "^144.0.0",
"node-fetch": "^2.6.6",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"winston": "^3.14.2"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@types/eslint__js": "^8.42.3",
"@types/node": "^22.5.5",
"@types/node-fetch": "^2.6.11",
"eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"prettier": "^3.3.3",
"shx": "^0.3.4",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.6.0",
"typescript-eslint": "^8.8.0"
},
"files": [
"dist"
]
}
18 changes: 9 additions & 9 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/* eslint-env node */
module.exports = {
bracketSpacing: false,
printWidth: 90,
semi: true,
singleQuote: true,
tabWidth: 4,
trailingComma: 'es5',
useTabs: false,
arrowParens: 'avoid',
};
bracketSpacing: false,
printWidth: 90,
semi: true,
singleQuote: true,
tabWidth: 4,
trailingComma: 'es5',
useTabs: false,
arrowParens: 'avoid',
};
37 changes: 23 additions & 14 deletions src/auth/client.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import fs from 'fs/promises';
import {authenticate} from '@google-cloud/local-auth';
import {google, Auth} from 'googleapis';
import {SCOPES, TOKEN_PATH, CREDENTIALS_PATH} from '../config/config';
import {Logger} from '../utils/logger';
import {logger} from '../utils/logger';

const logger = Logger.getLogger();
export interface AuthClientConfig {
tokenPath: string;
credentialsPath: string;
}

export async function loadSavedCredentialsIfExist(): Promise<Auth.OAuth2Client | null> {
export async function loadSavedCredentialsIfExist(
tokenPath: string
): Promise<Auth.OAuth2Client | null> {
try {
const content = await fs.readFile(TOKEN_PATH, 'utf-8');
const content = await fs.readFile(tokenPath, 'utf-8');
const credentials = JSON.parse(content);
const client = google.auth.fromJSON(credentials) as Auth.OAuth2Client;
client.setCredentials(credentials);
Expand All @@ -20,9 +24,12 @@ export async function loadSavedCredentialsIfExist(): Promise<Auth.OAuth2Client |
}
}

export async function saveCredentials(client: Auth.OAuth2Client): Promise<void> {
export async function saveCredentials(
client: Auth.OAuth2Client,
tokenPath: string
): Promise<void> {
try {
const content = await fs.readFile(CREDENTIALS_PATH, 'utf-8');
const content = await fs.readFile(tokenPath, 'utf-8');
const keys = JSON.parse(content);
const key = keys.installed || keys.web;

Expand All @@ -35,25 +42,27 @@ export async function saveCredentials(client: Auth.OAuth2Client): Promise<void>
expiry_date: client.credentials.expiry_date,
});

await fs.writeFile(TOKEN_PATH, payload);
await fs.writeFile(tokenPath, payload);
logger.info('Credentials saved successfully.');
} catch (err) {
logger.error('Error saving credentials:', err);
throw new Error('Failed to save credentials.');
}
}

export async function authorize(): Promise<Auth.OAuth2Client> {
let client = await loadSavedCredentialsIfExist();
export async function authorize(config: AuthClientConfig): Promise<Auth.OAuth2Client> {
const {tokenPath, credentialsPath} = config;
let client = await loadSavedCredentialsIfExist(tokenPath);

if (client) {
try {
await client.getAccessToken();
return client;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (err?.response?.data?.error === 'invalid_grant') {
logger.warn('Invalid refresh token. Reauthenticating...');
await fs.unlink(TOKEN_PATH).catch(() => {
await fs.unlink(tokenPath).catch(() => {
logger.warn('Token file does not exist.');
});
client = null;
Expand All @@ -66,12 +75,12 @@ export async function authorize(): Promise<Auth.OAuth2Client> {

if (!client) {
client = await authenticate({
scopes: SCOPES,
keyfilePath: CREDENTIALS_PATH,
scopes: ['https://www.googleapis.com/auth/drive'],
keyfilePath: credentialsPath,
});

if (client.credentials) {
await saveCredentials(client);
await saveCredentials(client, tokenPath);
return client;
} else {
throw new Error(
Expand Down
20 changes: 0 additions & 20 deletions src/config/config.ts

This file was deleted.

52 changes: 40 additions & 12 deletions src/database/database.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import sqlite3 from 'sqlite3';
import {open, Database, Statement} from 'sqlite';
import {DATABASE_PATH, FOLDER_IDS} from '../config/config';
import {DatabaseFile, RefreshResult} from './models';
import {GoogleDriveService} from '../services/googleDriveService';
import {GoogleDriveService} from '../services/google-drive';
import {DatabaseError} from '../errors';
import {DatabaseFile, RefreshResult} from './models';
import {GoogleFile} from '../types';
import {escapeSingleQuotes} from '../utils';
import {Logger} from '../utils/logger';

const logger = Logger.getLogger();
import {logger} from '../utils/logger';

type SQLiteDB = Database<sqlite3.Database, sqlite3.Statement>;
type SQLiteStmt = Statement;

export type {DatabaseFile, RefreshResult} from './models';

export class FolderDatabase {
private db!: SQLiteDB;
private googleDriveService: GoogleDriveService;

constructor(googleDriveService: GoogleDriveService) {
private folderId: string;
private databasePath: string;

constructor(
googleDriveService: GoogleDriveService,
folderId: string,
databasePath: string
) {
this.googleDriveService = googleDriveService;
this.folderId = folderId;
this.databasePath = databasePath;
}

/**
Expand All @@ -27,7 +34,7 @@ export class FolderDatabase {
async initDatabase(): Promise<void> {
try {
this.db = await open({
filename: DATABASE_PATH,
filename: this.databasePath,
driver: sqlite3.Database,
});

Expand Down Expand Up @@ -79,7 +86,7 @@ export class FolderDatabase {
webViewLink = excluded.webViewLink;
`;

let update: SQLiteStmt; // Use the correct Statement type
let update: SQLiteStmt;
try {
update = await this.db.prepare(insertStmt);
} catch (err) {
Expand Down Expand Up @@ -142,8 +149,7 @@ export class FolderDatabase {
async refresh(): Promise<RefreshResult> {
try {
logger.info('Starting database refresh...');
const {folderMap, folderIds, files} =
await this.googleDriveService.fetchAllFiles(FOLDER_IDS);
const {files} = await this.googleDriveService.fetchAllFiles([this.folderId]);

const existingIds = await this.getExistingFileIds();
const fetchedIds = new Set(files.map(file => file.id));
Expand Down Expand Up @@ -199,4 +205,26 @@ export class FolderDatabase {
throw new DatabaseError(`Search query failed: ${(err as Error).message}`);
}
}

/**
* Checks if a file exists in the database based on its webViewLink.
* @param fileLink The webViewLink of the file.
* @returns Boolean indicating existence.
*/
async fileExists(fileLink: string): Promise<boolean> {
const fileId = this.googleDriveService.extractFileIdFromLink(fileLink);
if (!fileId) return false;

try {
const result = await this.db.get(`SELECT id FROM files WHERE id = ?;`, [
fileId,
]);
return !!result;
} catch (err) {
logger.error('Error checking file existence:', err);
throw new DatabaseError(
`Failed to check file existence: ${(err as Error).message}`
);
}
}
}
Loading

0 comments on commit e8b7ba0

Please sign in to comment.