A decentralized music tracking and discovery platform built on AT Protocol 🎵

[api] implement googledrive and dropbox xrpc api

+248 -30
+23
apps/api/src/schema/dropbox-directories.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + 4 + const dropboxDirectories = pgTable("dropbox_directories", { 5 + id: text("xata_id").primaryKey(), 6 + name: text("name").notNull(), 7 + path: text("path").notNull(), 8 + parentId: text("parent_id").references(() => dropboxDirectories.id), 9 + dropboxId: text("dropbox_id").notNull(), 10 + fileId: text("file_id").notNull().unique(), 11 + xataVersion: text("xata_version").notNull(), 12 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 13 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 14 + }); 15 + 16 + export type SelectDropboxDirectories = InferSelectModel< 17 + typeof dropboxDirectories 18 + >; 19 + export type InsertDropboxDirectories = InferInsertModel< 20 + typeof dropboxDirectories 21 + >; 22 + 23 + export default dropboxDirectories;
+21
apps/api/src/schema/dropbox-paths.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + import dropboxDirectories from "./dropbox-directories"; 4 + 5 + const dropboxPaths = pgTable("dropbox_paths", { 6 + id: text("xata_id").primaryKey(), 7 + path: text("path").notNull(), 8 + name: text("name").notNull(), 9 + dropboxId: text("dropbox_id").notNull(), 10 + trackId: text("track_id").notNull(), 11 + directoryId: text("directory_id").references(() => dropboxDirectories.id), 12 + fileId: text("file_id").notNull().unique(), 13 + xataVersion: text("xata_version").notNull(), 14 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 15 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 16 + }); 17 + 18 + export type SelectDropboxPaths = InferSelectModel<typeof dropboxPaths>; 19 + export type InsertDropboxDirectories = InferInsertModel<typeof dropboxPaths>; 20 + 21 + export default dropboxPaths;
+22
apps/api/src/schema/dropbox.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + import dropboxTokens from "./dropbox-tokens"; 4 + import users from "./users"; 5 + 6 + const dropbox = pgTable("dropbox", { 7 + id: text("xata_id").primaryKey(), 8 + userId: text("user_id") 9 + .notNull() 10 + .references(() => users.id), 11 + dropboxTokenId: text("dropbox_token_id") 12 + .notNull() 13 + .references(() => dropboxTokens.id), 14 + xataVersion: text("xata_version").notNull(), 15 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 16 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 17 + }); 18 + 19 + export type SelectDropbox = InferSelectModel<typeof dropbox>; 20 + export type InsertDropbox = InferInsertModel<typeof dropbox>; 21 + 22 + export default dropbox;
+23
apps/api/src/schema/google-drive-directories.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + 4 + const googleDriveDirectories = pgTable("google_drive_directories", { 5 + id: text("xata_id").primaryKey(), 6 + name: text("name").notNull(), 7 + path: text("path").notNull(), 8 + parentId: text("parent_id").references(() => googleDriveDirectories.id), 9 + googleDriveId: text("google_drive_id").notNull(), 10 + fileId: text("file_id").notNull().unique(), 11 + xataVersion: text("xata_version").notNull(), 12 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 13 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 14 + }); 15 + 16 + export type SelectGoogleDriveDirectories = InferSelectModel< 17 + typeof googleDriveDirectories 18 + >; 19 + export type InsertGoogleDriveDirectories = InferInsertModel< 20 + typeof googleDriveDirectories 21 + >; 22 + 23 + export default googleDriveDirectories;
+20
apps/api/src/schema/google-drive-paths.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + import googleDriveDirectories from "./google-drive-directories"; 4 + 5 + const googleDrivePaths = pgTable("google_drive_paths", { 6 + id: text("xata_id").primaryKey(), 7 + googleDriveId: text("google_drive_id").notNull(), 8 + trackId: text("track_id").notNull(), 9 + name: text("name").notNull(), 10 + directoryId: text("directory_id").references(() => googleDriveDirectories.id), 11 + fileId: text("file_id").notNull().unique(), 12 + xataVersion: text("xata_version").notNull(), 13 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 14 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 15 + }); 16 + 17 + export type SelectGoogleDrivePaths = InferSelectModel<typeof googleDrivePaths>; 18 + export type InsertGoogleDrivePaths = InferInsertModel<typeof googleDrivePaths>; 19 + 20 + export default googleDrivePaths;
+22
apps/api/src/schema/googledrive.ts
··· 1 + import { InferInsertModel, InferSelectModel } from "drizzle-orm"; 2 + import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; 3 + import googleDriveTokens from "./google-drive-tokens"; 4 + import users from "./users"; 5 + 6 + const googleDrive = pgTable("google_drive", { 7 + id: text("xata_id").primaryKey(), 8 + googleDriveTokenId: text("google_drive_token_id") 9 + .notNull() 10 + .references(() => googleDriveTokens.id), 11 + userId: text("user_id") 12 + .notNull() 13 + .references(() => users.id), 14 + xataVersion: text("xata_version").notNull(), 15 + createdAt: timestamp("xata_createdat").defaultNow().notNull(), 16 + updatedAt: timestamp("xata_updatedat").defaultNow().notNull(), 17 + }); 18 + 19 + export type SelectGoogleDrive = InferSelectModel<typeof googleDrive>; 20 + export type InsertGoogleDrive = InferInsertModel<typeof googleDrive>; 21 + 22 + export default googleDrive;
+12
apps/api/src/schema/index.ts
··· 4 4 import artistAlbums from "./artist-albums"; 5 5 import artistTracks from "./artist-tracks"; 6 6 import artists from "./artists"; 7 + import dropbox from "./dropbox"; 7 8 import dropboxAccounts from "./dropbox-accounts"; 9 + import dropboxDirectories from "./dropbox-directories"; 10 + import dropboxPaths from "./dropbox-paths"; 8 11 import googleDriveAccounts from "./google-drive-accounts"; 12 + import googleDriveDirectories from "./google-drive-directories"; 13 + import googleDrivePaths from "./google-drive-paths"; 14 + import googleDrive from "./googledrive"; 9 15 import lovedTracks from "./loved-tracks"; 10 16 import playlistTracks from "./playlist-tracks"; 11 17 import playlists from "./playlists"; ··· 50 56 artistAlbums, 51 57 dropboxAccounts, 52 58 googleDriveAccounts, 59 + dropboxDirectories, 60 + dropboxPaths, 61 + googleDriveDirectories, 62 + googleDrivePaths, 63 + dropbox, 64 + googleDrive, 53 65 };
+44 -18
apps/api/src/xrpc/app/rocksky/dropbox/getFiles.ts
··· 1 1 import { HandlerAuth } from "@atproto/xrpc-server"; 2 2 import { Context } from "context"; 3 - import { eq } from "drizzle-orm"; 3 + import { and, eq } from "drizzle-orm"; 4 4 import { Effect, pipe } from "effect"; 5 5 import { Server } from "lexicon"; 6 6 import { QueryParams } from "lexicon/types/app/rocksky/dropbox/getFiles"; ··· 8 8 9 9 export default function (server: Server, ctx: Context) { 10 10 const getFiles = (params, auth: HandlerAuth) => 11 - pipe(params, retrieve, presentation); 11 + pipe( 12 + { params, ctx, did: auth.credentials?.did }, 13 + retrieve, 14 + presentation, 15 + Effect.retry({ times: 3 }), 16 + Effect.timeout("10 seconds"), 17 + Effect.catchAll((err) => { 18 + console.error(err); 19 + return Effect.succeed({ artists: [] }); 20 + }) 21 + ); 12 22 server.app.rocksky.dropbox.getFiles({ 13 23 auth: ctx.authVerifier, 14 24 handler: async ({ params, auth }) => { 15 - const result = getFiles(params, auth); 25 + const result = await Effect.runPromise(getFiles(params, auth)); 16 26 return { 17 27 encoding: "application/json", 18 28 body: result, ··· 21 31 }); 22 32 } 23 33 24 - const getCurrentUser = ({ 34 + const retrieve = ({ 25 35 params, 26 36 ctx, 27 37 did, 28 38 }: { 29 39 params: QueryParams; 30 40 ctx: Context; 31 - did?: string; 41 + did: string; 32 42 }) => { 33 43 return Effect.tryPromise({ 34 44 try: async () => 35 45 ctx.db 36 46 .select() 37 - .from(tables.users) 38 - .where(eq(tables.users.did, did)) 39 - .execute() 40 - .then((users) => ({ user: users[0], ctx, params })), 41 - catch: (error) => new Error(`Failed to retrieve current user: ${error}`), 47 + .from(tables.dropboxDirectories) 48 + .leftJoin( 49 + tables.dropbox, 50 + eq(tables.dropbox.id, tables.dropboxDirectories.dropboxId) 51 + ) 52 + .leftJoin(tables.users, eq(tables.dropbox.userId, tables.users.id)) 53 + .where( 54 + and( 55 + eq(tables.users.did, did), 56 + eq(tables.dropboxDirectories.path, params.at) 57 + ) 58 + ) 59 + .execute(), 60 + catch: (error) => { 61 + console.error("Failed to retrieve files:", error); 62 + return new Error(`Failed to retrieve files: ${error}`); 63 + }, 42 64 }); 43 65 }; 44 66 45 - const retrieve = () => { 46 - // Logic to retrieve files from Dropbox 47 - return []; 48 - }; 49 - 50 - const presentation = (files) => { 51 - // Logic to format the files for presentation 52 - return {}; 67 + const presentation = (data) => { 68 + return Effect.sync(() => ({ 69 + files: data.map((item) => ({ 70 + id: item.dropbox_directories.id, 71 + name: item.dropbox_directories.name, 72 + fileId: item.dropbox_directories.fileId, 73 + path: item.dropbox_directories.path, 74 + parentId: item.dropbox_directories.parentId, 75 + createdAt: item.dropbox_directories.xata_createdat, 76 + updatedAt: item.dropbox_directories.xata_updatedat, 77 + })), 78 + })); 53 79 };
+61 -12
apps/api/src/xrpc/app/rocksky/googledrive/getFiles.ts
··· 1 1 import { Context } from "context"; 2 - import { pipe } from "effect"; 2 + import { and, eq } from "drizzle-orm"; 3 + import { Effect, pipe } from "effect"; 3 4 import { Server } from "lexicon"; 5 + import { QueryParams } from "lexicon/types/app/rocksky/googledrive/getFiles"; 6 + import tables from "schema"; 4 7 5 8 export default function (server: Server, ctx: Context) { 6 - const getFiles = (params) => pipe(params, retrieve, presentation); 9 + const getFiles = (params, auth) => 10 + pipe( 11 + { params, ctx, did: auth.credentials?.did }, 12 + retrieve, 13 + presentation, 14 + Effect.retry({ times: 3 }), 15 + Effect.timeout("10 seconds"), 16 + Effect.catchAll((err) => { 17 + console.error(err); 18 + return Effect.succeed({ artists: [] }); 19 + }) 20 + ); 7 21 server.app.rocksky.googledrive.getFiles({ 8 22 auth: ctx.authVerifier, 9 - handler: async ({ params }) => { 10 - const result = getFiles(params); 23 + handler: async ({ params, auth }) => { 24 + const result = await Effect.runPromise(getFiles(params, auth)); 11 25 return { 12 26 encoding: "application/json", 13 27 body: result, ··· 16 30 }); 17 31 } 18 32 19 - const retrieve = () => { 20 - // Logic to retrieve files from Google Drive 21 - return []; 33 + const retrieve = ({ 34 + params, 35 + ctx, 36 + did, 37 + }: { 38 + params: QueryParams; 39 + ctx: Context; 40 + did: string; 41 + }) => { 42 + return Effect.tryPromise({ 43 + try: async () => 44 + ctx.db 45 + .select() 46 + .from(tables.googleDriveDirectories) 47 + .leftJoin( 48 + tables.googleDrive, 49 + eq(tables.googleDrive.id, tables.googleDriveDirectories.googleDriveId) 50 + ) 51 + .leftJoin(tables.users, eq(tables.googleDrive.userId, tables.users.id)) 52 + .where( 53 + and( 54 + eq(tables.users.did, did), 55 + eq(tables.googleDriveDirectories.path, params.at) 56 + ) 57 + ) 58 + .execute(), 59 + catch: (error) => { 60 + console.error("Failed to retrieve files:", error); 61 + return new Error(`Failed to retrieve albums: ${error}`); 62 + }, 63 + }); 22 64 }; 23 65 24 - const presentation = (files) => { 25 - // Logic to format the files for presentation 26 - return { 27 - files: files, 28 - }; 66 + const presentation = (data) => { 67 + return Effect.sync(() => ({ 68 + files: data.map((item) => ({ 69 + id: item.google_drive_directories.id, 70 + name: item.google_drive_directories.name, 71 + fileId: item.google_drive_directories.fileId, 72 + path: item.google_drive_directories.path, 73 + parentId: item.google_drive_directories.parentId, 74 + createdAt: item.google_drive_directories.xata_createdat, 75 + updatedAt: item.google_drive_directories.xata_updatedat, 76 + })), 77 + })); 29 78 };