Skip to content

Commit 00fcd7d

Browse files
committed
feat: Allow to send Pouch databases through email for debug purpose
By adding offline support through PouchDB, we expect database related bugs to happens in the future In order to ease debugging them, we want to allow exploring the local PouchDB files The easier way is to add the ability to extract them from the device and send them through email to cozy's support team
1 parent ca76c14 commit 00fcd7d

File tree

4 files changed

+141
-0
lines changed

4 files changed

+141
-0
lines changed

src/hooks/useAppBootstrap.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { useEffect, useState } from 'react'
55
import { deconstructCozyWebLinkWithSlug } from 'cozy-client'
66

77
import { handleLogsDeepLink } from '/app/domain/logger/deeplinkHandler'
8+
import { handleDbDeepLink } from '/pouchdb/deeplinkHandler'
89
import { SentryCustomTags, setSentryTag } from '/libs/monitoring/Sentry'
910
import { manageIconCache } from '/libs/functions/iconTable'
1011
import { getDefaultIconParams } from '/libs/functions/openApp'
@@ -160,6 +161,10 @@ export const useAppBootstrap = client => {
160161
return
161162
}
162163

164+
if (handleDbDeepLink(url, client)) {
165+
return
166+
}
167+
163168
if (!client) {
164169
const action = parseOnboardLink(url)
165170

src/hooks/useAppBootstrap.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ jest.mock('/libs/RootNavigation', () => ({
5050
jest.mock('./useSplashScreen', () => ({
5151
useSplashScreen: () => ({ hideSplashScreen: mockHideSplashScreen })
5252
}))
53+
jest.mock('/app/theme/SplashScreenService', () => ({}))
5354

5455
jest.mock('/libs/functions/openApp', () => ({
5556
getDefaultIconParams: jest.fn().mockReturnValue({})

src/pouchdb/deeplinkHandler.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import CozyClient from 'cozy-client'
2+
3+
import strings from '/constants/strings.json'
4+
import { sendDbByEmail } from '/pouchdb/sendDbByEmail'
5+
6+
export const handleDbDeepLink = (url: string, client?: CozyClient): boolean => {
7+
if (isSendDbDeepLink(url)) {
8+
void sendDbByEmail(client)
9+
10+
return true
11+
}
12+
13+
return false
14+
}
15+
16+
const isSendDbDeepLink = (url: string): boolean => {
17+
const deepLinks = [
18+
`${strings.COZY_SCHEME}senddb`,
19+
`${strings.UNIVERSAL_LINK_BASE}/senddb`
20+
]
21+
22+
return deepLinks.includes(url.toLowerCase())
23+
}

src/pouchdb/sendDbByEmail.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { Alert, PermissionsAndroid } from 'react-native'
2+
import Mailer from 'react-native-mail'
3+
import RNFS from 'react-native-fs'
4+
import RNFetchBlob from 'rn-fetch-blob'
5+
import DeviceInfo from 'react-native-device-info'
6+
7+
import type CozyClient from 'cozy-client'
8+
import Minilog from 'cozy-minilog'
9+
10+
import { fetchSupportMail } from '/app/domain/logger/supportEmail'
11+
import {
12+
hideSplashScreen,
13+
showSplashScreen,
14+
splashScreens
15+
} from '/app/theme/SplashScreenService'
16+
import { getInstanceAndFqdnFromClient } from '/libs/client'
17+
18+
const log = Minilog('🗒️ DB Mailer')
19+
20+
export const sendDbByEmail = async (client?: CozyClient): Promise<void> => {
21+
log.info('Send DB by email')
22+
23+
if (!client) {
24+
log.info('SendDbByEmail called with no client, return')
25+
return
26+
}
27+
try {
28+
29+
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
30+
await PermissionsAndroid.request(permission)
31+
32+
const supportEmail = await fetchSupportMail(client)
33+
34+
const { fqdn } = getInstanceAndFqdnFromClient(client)
35+
36+
const instance = client.getStackClient().uri ?? 'not logged app'
37+
38+
const subject = `DB file for ${instance}`
39+
40+
const files = await RNFS.readDir(RNFS.DocumentDirectoryPath)
41+
42+
const dbFiles = files.filter(f => f.name.startsWith(`${fqdn}_`))
43+
44+
const externalFiles = []
45+
for (const dbFile of dbFiles) {
46+
const dirs = RNFetchBlob.fs.dirs
47+
48+
const internalPath = dbFile.path
49+
50+
const date = Number(new Date())
51+
const externalPath = `${dirs.DCIMDir}/DbFile_${dbFile.name}${date}.sqlite`
52+
53+
await RNFS.copyFile(internalPath, externalPath)
54+
55+
externalFiles.push({
56+
path: externalPath
57+
})
58+
}
59+
60+
await showSplashScreen(splashScreens.SEND_LOG_EMAIL)
61+
log.info('Start email intent')
62+
Mailer.mail(
63+
{
64+
subject: subject,
65+
recipients: [supportEmail],
66+
body: buildMessageBody(),
67+
customChooserTitle: 'This is my new title', // Android only (defaults to "Send Mail")
68+
isHTML: true,
69+
attachments: externalFiles
70+
},
71+
(error, event) => {
72+
Alert.alert(
73+
error,
74+
event,
75+
[
76+
{
77+
text: 'Ok',
78+
onPress: (): void => log.debug('OK: Email Error Response')
79+
},
80+
{
81+
text: 'Cancel',
82+
onPress: (): void => log.debug('CANCEL: Email Error Response')
83+
}
84+
],
85+
{ cancelable: true }
86+
)
87+
}
88+
)
89+
log.info('Did finish email intent')
90+
await hideSplashScreen(splashScreens.SEND_LOG_EMAIL)
91+
92+
}
93+
catch (err) {
94+
console.log('🍎 ERORR WHILE EMAIL', err.message)
95+
}
96+
}
97+
98+
const buildMessageBody = (): string => {
99+
const appVersion = DeviceInfo.getVersion()
100+
const appBuild = DeviceInfo.getBuildNumber()
101+
const bundle = DeviceInfo.getBundleId()
102+
const deviceBrand = DeviceInfo.getBrand()
103+
const deviceModel = DeviceInfo.getModel()
104+
const os = DeviceInfo.getSystemName()
105+
const version = DeviceInfo.getSystemVersion()
106+
107+
const appInfo = `App info: ${appVersion} (${appBuild})`
108+
const bundleInfo = `App bundle: ${bundle}`
109+
const deviceInfo = `Device info: ${deviceBrand} ${deviceModel} ${os} ${version}`
110+
111+
return `${appInfo}\n${bundleInfo}\n${deviceInfo}`
112+
}

0 commit comments

Comments
 (0)