From 3c29b6723efb62de2b7fffbbbc6955a8e3a66f99 Mon Sep 17 00:00:00 2001 From: Piotr Rogowski Date: Fri, 14 Oct 2022 12:01:18 +0200 Subject: [PATCH] Appwrite 1.0 (#793) --- package-lock.json | 62 +++++++++++----------- package.json | 2 +- src/appwrite.ts | 6 +-- src/contexts/AuthContext.tsx | 96 ++++++----------------------------- src/hooks/useDb.ts | 37 +++++++++----- src/hooks/useServerStorage.ts | 15 ++++-- src/pages/Hub.tsx | 2 +- src/pages/Upload.tsx | 1 + src/pages/auth/Profile.tsx | 2 +- 9 files changed, 89 insertions(+), 134 deletions(-) diff --git a/package-lock.json b/package-lock.json index 42705d2..5f7903b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@sentry/react": "^7.15.0", "@sentry/tracing": "^7.15.0", "antd": "^4.23.5", - "appwrite": "9.0.2", + "appwrite": "10.1.0", "kbar": "^0.1.0-beta.36", "lodash.debounce": "^4.0.8", "mlg-converter": "^0.5.1", @@ -1258,9 +1258,9 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "3.1.0", @@ -2079,9 +2079,9 @@ } }, "node_modules/appwrite": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-9.0.2.tgz", - "integrity": "sha512-3mhI9eNzOz8k9d2RDuYQdA9BHCgmT3HvtsVdxHcY20ps8TnEj38jx3NilQ/60G6ALrdMt79u9mYIRsj4ftitTw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-10.1.0.tgz", + "integrity": "sha512-kHtPqKf0X+mxmkS47G3F5vVY5wKMVRv7ZTpTvd9H3m1KBIm3aDAEBCEUt6bGQdE8XKgqLFzhqWFdQWkxX6I0xA==", "dependencies": { "cross-fetch": "3.1.5", "isomorphic-form-data": "2.0.0" @@ -2648,9 +2648,9 @@ "integrity": "sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA==" }, "node_modules/electron-to-chromium": { - "version": "1.4.281", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.281.tgz", - "integrity": "sha512-yer0w5wCYdFoZytfmbNhwiGI/3cW06+RV7E23ln4490DVMxs7PvYpbsrSmAiBn/V6gode8wvJlST2YfWgvzWIg==", + "version": "1.4.282", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.282.tgz", + "integrity": "sha512-Dki0WhHNh/br/Xi1vAkueU5mtIc9XLHcMKB6tNfQKk+kPG0TEUjRh5QEMAUbRp30/rYNMFD1zKKvbVzwq/4wmg==", "dev": true }, "node_modules/emoji-regex": { @@ -6442,9 +6442,9 @@ } }, "node_modules/rc-virtual-list": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz", - "integrity": "sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.10.tgz", + "integrity": "sha512-Jv0cgJxJ+8F/YViW8WGs/jQF2rmT8RUcJ5uDJs5MOFLTYLAvCpM/xU+Zu6EpCun50fmovhXiItQctcfE2UY3Aw==", "dependencies": { "classnames": "^2.2.6", "rc-resize-observer": "^1.0.0", @@ -6638,9 +6638,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", @@ -8655,9 +8655,9 @@ "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.16", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz", - "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "requires": { "@jridgewell/resolve-uri": "3.1.0", @@ -9245,9 +9245,9 @@ } }, "appwrite": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-9.0.2.tgz", - "integrity": "sha512-3mhI9eNzOz8k9d2RDuYQdA9BHCgmT3HvtsVdxHcY20ps8TnEj38jx3NilQ/60G6ALrdMt79u9mYIRsj4ftitTw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/appwrite/-/appwrite-10.1.0.tgz", + "integrity": "sha512-kHtPqKf0X+mxmkS47G3F5vVY5wKMVRv7ZTpTvd9H3m1KBIm3aDAEBCEUt6bGQdE8XKgqLFzhqWFdQWkxX6I0xA==", "requires": { "cross-fetch": "3.1.5", "isomorphic-form-data": "2.0.0" @@ -9666,9 +9666,9 @@ "integrity": "sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA==" }, "electron-to-chromium": { - "version": "1.4.281", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.281.tgz", - "integrity": "sha512-yer0w5wCYdFoZytfmbNhwiGI/3cW06+RV7E23ln4490DVMxs7PvYpbsrSmAiBn/V6gode8wvJlST2YfWgvzWIg==", + "version": "1.4.282", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.282.tgz", + "integrity": "sha512-Dki0WhHNh/br/Xi1vAkueU5mtIc9XLHcMKB6tNfQKk+kPG0TEUjRh5QEMAUbRp30/rYNMFD1zKKvbVzwq/4wmg==", "dev": true }, "emoji-regex": { @@ -12200,9 +12200,9 @@ } }, "rc-virtual-list": { - "version": "3.4.8", - "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz", - "integrity": "sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg==", + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/rc-virtual-list/-/rc-virtual-list-3.4.10.tgz", + "integrity": "sha512-Jv0cgJxJ+8F/YViW8WGs/jQF2rmT8RUcJ5uDJs5MOFLTYLAvCpM/xU+Zu6EpCun50fmovhXiItQctcfE2UY3Aw==", "requires": { "classnames": "^2.2.6", "rc-resize-observer": "^1.0.0", @@ -12332,9 +12332,9 @@ "requires": {} }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.10", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==" }, "regexp.prototype.flags": { "version": "1.4.3", diff --git a/package.json b/package.json index 7ee14bf..0dec48a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@sentry/react": "^7.15.0", "@sentry/tracing": "^7.15.0", "antd": "^4.23.5", - "appwrite": "9.0.2", + "appwrite": "10.1.0", "kbar": "^0.1.0-beta.36", "lodash.debounce": "^4.0.8", "mlg-converter": "^0.5.1", diff --git a/src/appwrite.ts b/src/appwrite.ts index ce31448..8277eba 100644 --- a/src/appwrite.ts +++ b/src/appwrite.ts @@ -10,15 +10,15 @@ const client = new Client(); client .setEndpoint(fetchEnv('VITE_APPWRITE_ENDPOINT')) - .setProject('hyper-tuner-cloud'); + .setProject('hyper-tuner-api'); const account = new Account(client); -const database = new Databases(client, 'public'); +const databases = new Databases(client); const storage = new Storage(client); export { client, account, - database, + databases, storage, }; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 0d3613c..785031a 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,3 +1,7 @@ +import { + ID, + Models, +} from 'appwrite'; import { createContext, ReactNode, @@ -17,84 +21,16 @@ import { buildRedirectUrl, } from '../utils/url'; -export interface User { - $id: string; - name: string; - registration: number; - status: boolean; - passwordUpdate: number; - email: string; - emailVerification: boolean; - prefs: {}; -} - -export interface Session { - $id: string; - userId: string; - expire: number; - provider: string; - providerUid: string; - providerAccessToken: string; - providerAccessTokenExpiry: number; - providerRefreshToken: string; - ip: string; - osCode: string; - osName: string; - osVersion: string; - clientType: string; - clientCode: string; - clientName: string; - clientVersion: string; - clientEngine: string; - clientEngineVersion: string; - deviceName: string; - deviceBrand: string; - deviceModel: string; - countryCode: string; - countryName: string; - current: boolean; -}; - -export interface SessionList { - sessions: Session[]; - total: number; -}; - -export interface Log { - event: string; - userId: string; - userEmail: string; - userName: string; - mode: string; - ip: string; - time: number; - osCode: string; - osName: string; - osVersion: string; - clientType: string; - clientCode: string; - clientName: string; - clientVersion: string; - clientEngine: string; - clientEngineVersion: string; - deviceName: string; - deviceBrand: string; - deviceModel: string; - countryCode: string; - countryName: string; -}; - -export interface LogList { - logs: Log[]; - total: number; -} +export type SessionList = Models.SessionList; +export type LogList = Models.LogList; +export type Account = Models.Account; interface AuthValue { - currentUser: User | null, - signUp: (email: string, password: string, username: string) => Promise, - login: (email: string, password: string) => Promise, + currentUser: Account | null, + signUp: (email: string, password: string, username: string) => Promise, + login: (email: string, password: string) => Promise, sendMagicLink: (email: string) => Promise, - confirmMagicLink: (userId: string, secret: string) => Promise, + confirmMagicLink: (userId: string, secret: string) => Promise, sendEmailVerification: () => Promise, confirmEmailVerification: (userId: string, secret: string) => Promise, confirmResetPassword: (userId: string, secret: string, password: string) => Promise, @@ -124,14 +60,14 @@ const useAuth = () => useContext(AuthContext as any); const AuthProvider = (props: { children: ReactNode }) => { const { children } = props; - const [currentUser, setCurrentUser] = useState(null); + const [currentUser, setCurrentUser] = useState(null); const [isLoading, setIsLoading] = useState(true); const value = useMemo(() => ({ currentUser, signUp: async (email: string, password: string, username: string) => { try { - await account.create('unique()', email, password, username); + await account.create(ID.unique(), email, password, username); await account.createEmailSession(email, password); const user = await account.get(); setCurrentUser(user); @@ -152,7 +88,7 @@ const AuthProvider = (props: { children: ReactNode }) => { }, sendMagicLink: async (email: string) => { try { - await account.createMagicURLSession('unique()', email, MAGIC_LINK_REDIRECT_URL); + await account.createMagicURLSession(ID.unique(), email, MAGIC_LINK_REDIRECT_URL); return Promise.resolve(); } catch (error) { return Promise.reject(error); @@ -253,8 +189,8 @@ const AuthProvider = (props: { children: ReactNode }) => { return Promise.reject(error); } }, - getSessions: () => account.getSessions(), - getLogs: () => account.getLogs(), + getSessions: () => account.listSessions(), + getLogs: () => account.listLogs(), }), [currentUser]); useEffect(() => { diff --git a/src/hooks/useDb.ts b/src/hooks/useDb.ts index 15575dd..44d0a5c 100644 --- a/src/hooks/useDb.ts +++ b/src/hooks/useDb.ts @@ -1,9 +1,12 @@ import * as Sentry from '@sentry/browser'; import { + ID, Models, + Permission, Query, + Role, } from 'appwrite'; -import { database } from '../appwrite'; +import { databases } from '../appwrite'; import { TuneDbData, UsersBucket, @@ -12,13 +15,14 @@ import { } from '../types/dbData'; import { databaseGenericError } from '../pages/auth/notifications'; +const DB_ID = 'public'; const COLLECTION_ID_PUBLIC_TUNES = 'tunes'; const COLLECTION_ID_USERS_BUCKETS = 'usersBuckets'; const useDb = () => { const updateTune = async (documentId: string, data: TuneDbDataPartial) => { try { - await database.updateDocument(COLLECTION_ID_PUBLIC_TUNES, documentId, data); + await databases.updateDocument(DB_ID, COLLECTION_ID_PUBLIC_TUNES, documentId, data); return Promise.resolve(); } catch (error) { @@ -32,12 +36,15 @@ const useDb = () => { const createTune = async (data: TuneDbData) => { try { - const tune = await database.createDocument( + const tune = await databases.createDocument( + DB_ID, COLLECTION_ID_PUBLIC_TUNES, - 'unique()', + ID.unique(), data, - ['role:all'], - [`user:${data.userId}`], + [ + Permission.read(Role.any()), + Permission.write(Role.user(data.userId, 'verified')), + ], ); return Promise.resolve(tune); @@ -52,10 +59,13 @@ const useDb = () => { const getTune = async (tuneId: string) => { try { - const tune = await database.listDocuments( + const tune = await databases.listDocuments( + DB_ID, COLLECTION_ID_PUBLIC_TUNES, - [Query.equal('tuneId', tuneId)], - 1, + [ + Query.equal('tuneId', tuneId), + Query.limit(1), + ], ); return Promise.resolve(tune.total > 0 ? tune.documents[0] as unknown as TuneDbDocument : null); @@ -70,13 +80,14 @@ const useDb = () => { const getBucketId = async (userId: string) => { try { - const buckets = await database.listDocuments( + const buckets = await databases.listDocuments( + DB_ID, COLLECTION_ID_USERS_BUCKETS, [ Query.equal('userId', userId), Query.equal('visibility', 'public'), + Query.limit(1), ], - 1, ); if (buckets.total === 0) { @@ -100,8 +111,8 @@ const useDb = () => { try { const list: Models.DocumentList = await ( search - ? database.listDocuments(COLLECTION_ID_PUBLIC_TUNES, [Query.search('textSearch', search)], limit) - : database.listDocuments(COLLECTION_ID_PUBLIC_TUNES, [], limit) + ? databases.listDocuments(DB_ID, COLLECTION_ID_PUBLIC_TUNES, [Query.search('textSearch', search), Query.limit(limit)]) + : databases.listDocuments(DB_ID, COLLECTION_ID_PUBLIC_TUNES, [Query.limit(limit)]) ); return Promise.resolve(list); diff --git a/src/hooks/useServerStorage.ts b/src/hooks/useServerStorage.ts index 99d3e8f..6273ddd 100644 --- a/src/hooks/useServerStorage.ts +++ b/src/hooks/useServerStorage.ts @@ -1,6 +1,11 @@ import { notification } from 'antd'; import * as Sentry from '@sentry/browser'; -import { Models } from 'appwrite'; +import { + ID, + Models, + Permission, + Role, +} from 'appwrite'; import { storage } from '../appwrite'; import { fetchEnv } from '../utils/env'; @@ -65,10 +70,12 @@ const useServerStorage = () => { try { const createdFile = await storage.createFile( bucketId, - 'unique()', + ID.unique(), file, - ['role:all'], - [`user:${userId}`], + [ + Permission.read(Role.any()), + Permission.write(Role.user(userId, 'verified')), + ], ); return Promise.resolve(createdFile); diff --git a/src/pages/Hub.tsx b/src/pages/Hub.tsx index e07791c..a1e4877 100644 --- a/src/pages/Hub.tsx +++ b/src/pages/Hub.tsx @@ -59,7 +59,7 @@ const Hub = () => { author: 'John Doe', displacement: `${tune.displacement}l`, aspiration: aspirationMapper[tune.aspiration], - publishedAt: new Date(tune.$updatedAt * 1000).toLocaleString(), + publishedAt: new Date(tune.$updatedAt).toLocaleString(), stars: 0, }))); setIsLoading(false); diff --git a/src/pages/Upload.tsx b/src/pages/Upload.tsx index f7aafba..3c87d58 100644 --- a/src/pages/Upload.tsx +++ b/src/pages/Upload.tsx @@ -399,6 +399,7 @@ const UploadPage = () => { form.setFieldsValue(oldTune); setIsEditMode(true); setTuneDocumentId(oldTune.$id); + setReadme(oldTune.readme!); if (oldTune.tuneFileId) { const file = await getFile(oldTune.tuneFileId, await getBucketId(currentUser!.$id)); diff --git a/src/pages/auth/Profile.tsx b/src/pages/auth/Profile.tsx index c897ca9..27b4260 100644 --- a/src/pages/auth/Profile.tsx +++ b/src/pages/auth/Profile.tsx @@ -79,7 +79,7 @@ const Profile = () => { const fetchLogs = useCallback(async () => getLogs() .then((list) => setLogs(list.logs.slice(0, MAX_LIST_SIZE).map((log) => [ - new Date(log.time * 1000).toLocaleString(), + new Date(log.time).toLocaleString(), parseLogEvent(log.event), log.clientName, log.clientEngineVersion,