Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions src/main/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { app, dialog, shell } from 'electron'
import log from 'electron-log/main'
import { readdirSync } from 'fs'

import { getPlatform } from '@/utils/electron'
import { exhaustive, isNodeJsErrnoException } from '@/utils/typescript'

export function checkDocumentsAccess(): 'granted' | 'denied' | 'unknown' {
const docsPath = app.getPath('documents')

try {
readdirSync(docsPath)
return 'granted'
} catch (error) {
if (
isNodeJsErrnoException(error) &&
(error.code === 'EPERM' || error.code === 'EACCES')
) {
return 'denied'
}

log.error('Unexpected error while checking Documents access:', error)
return 'unknown'
}
}

async function openPermissionsSettings() {
const platform = getPlatform()
switch (platform) {
case 'mac':
await shell.openExternal(
'x-apple.systempreferences:com.apple.preference.security?Privacy_FilesAndFolders'
)
break
case 'win':
await shell.openExternal('ms-settings:privacy-documents')
break
case 'linux':
await dialog.showMessageBox({
type: 'info',
title: 'Open System Settings',
message: 'Open your system settings manually.',
detail:
'Look for Privacy or Permissions settings and grant file access to Grafana k6 Studio.',
})
break
default:
exhaustive(platform)
}
}

export async function verifyDocumentsAccess(): Promise<boolean> {
const accessStatus = checkDocumentsAccess()
const platform = getPlatform()

// Only show our custom dialog if access was explicitly denied
// If it's 'unknown', let the OS handle the permission prompt naturally
if (accessStatus === 'denied') {
let detailMessage = ''
switch (platform) {
case 'mac':
detailMessage =
'Grant Documents folder access in System Settings → Privacy & Security → Files and Folders.'
break
case 'win':
detailMessage =
'Grant file system access in Settings → Privacy → File system.'
break
case 'linux':
detailMessage = 'Grant file access permissions in your system settings.'
break
default:
exhaustive(platform)
}

const response = await dialog.showMessageBox({
type: 'warning',
title: 'Documents Folder Access Required',
message:
'Grafana k6 Studio needs access to your Documents folder to store files.',
detail: detailMessage,
buttons: ['Open System Settings', 'Quit'],
defaultId: 0,
cancelId: 1,
})

if (response.response === 0) {
await openPermissionsSettings()
}

return false
}

// If access is granted or unknown (OS will handle), return true
return true
}
7 changes: 7 additions & 0 deletions src/utils/workspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { existsSync } from 'fs'
import { mkdir } from 'fs/promises'
import path from 'path'

import { verifyDocumentsAccess } from '@/main/permissions'

import {
DATA_FILES_PATH,
PROJECT_PATH,
Expand All @@ -12,6 +14,11 @@ import {
} from '../constants/workspace'

export const setupProjectStructure = async () => {
const hasAccess = await verifyDocumentsAccess()
if (!hasAccess) {
throw new Error('Documents folder access denied')
}

if (!existsSync(PROJECT_PATH)) {
await mkdir(PROJECT_PATH)
}
Expand Down