Skip to content

Commit 41db019

Browse files
Dan PastusekDan Pastusek
authored andcommitted
add SMB fixes + timemachine
1 parent f860b08 commit 41db019

File tree

7 files changed

+180
-95
lines changed

7 files changed

+180
-95
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "pibox-host",
3-
"version": "1.44.0",
3+
"version": "1.45.0",
44
"private": true,
55
"bin": "server.js",
66
"scripts": {

src/functions.js

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,99 @@ import randomColor from 'randomcolor'
1212
import c from 'chalk'
1313
import { setTimeout as setTimeoutPromise } from 'timers/promises'
1414

15+
export async function saveSambaConfig() {
16+
const { usedSpace, totalSpace } = await getStorage()
17+
const remainingSpace = (totalSpace - usedSpace) * 1024
18+
const config = await getConfig()
19+
const owner = await getOwner()
20+
let smbConfig = `# Group Name to User mapping`
21+
smbConfig += config.groups.map((group) => `# ${group.groupName} => ${group.usersString}\n`)
22+
smbConfig += `\n\n`
23+
smbConfig = `[global]
24+
netbios name = PIBOX
25+
workgroup = WORKGROUP
26+
access based share enum = yes
27+
logging = syslog
28+
server role = standalone server
29+
veto files = /._*/.DS_Store/
30+
delete veto files = yes
31+
32+
fruit:aapl = yes
33+
fruit:nfs_aces = no
34+
fruit:copyfile = no
35+
fruit:model = PiBox
36+
inherit permissions = yes
37+
multicast dns register = no
38+
39+
[PiBox Time Machine]
40+
vfs objects = catia fruit streams_xattr
41+
fruit:time machine = yes
42+
fruit:time machine max size = ${remainingSpace}
43+
comment = Time Machine Backup
44+
path = /pibox/timemachine
45+
available = yes
46+
valid users = @${owner}
47+
browseable = yes
48+
guest ok = no
49+
writable = yes
50+
51+
[Files]
52+
path = /pibox/files
53+
read only = no
54+
valid users = @${owner}
55+
\n`
56+
57+
config.shares.forEach((share) => {
58+
smbConfig += `[${share.name}]
59+
path = ${PIBOX_FILES_PREFIX + share.path}
60+
read only = no
61+
valid users = @${share.groupName}\n\n`
62+
})
63+
64+
await writeFile('/etc/samba/smb.conf', smbConfig)
65+
await execAndLog('global:samba', 'systemctl restart smbd')
66+
}
67+
68+
export async function ensureTimeMachine() {
69+
const owner = await getOwner()
70+
const avahiConfig = `<?xml version="1.0" standalone='no'?>
71+
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
72+
<service-group>
73+
<name replace-wildcards="yes">%h</name>
74+
<service>
75+
<type>_smb._tcp</type>
76+
<port>445</port>
77+
</service>
78+
<service>
79+
<type>_device-info._tcp</type>
80+
<port>0</port>
81+
<txt-record>model=RackMac</txt-record>
82+
</service>
83+
<service>
84+
<type>_adisk._tcp</type>
85+
<txt-record>dk0=adVN=PiBox Time Machine,adVF=0x82</txt-record>
86+
<txt-record>sys=waMa=0,adVF=0x100</txt-record>
87+
</service>
88+
</service-group>`
89+
await writeFile('/etc/avahi/services/samba.service', avahiConfig)
90+
await execAndLog('global:samba', 'systemctl restart avahi-daemon')
91+
await execAndLog('global:samba', 'mkdir -p /pibox/timemachine')
92+
await execAndLog('global:samba', `chown ${owner}:${owner} /pibox/timemachine`)
93+
}
94+
1595
export const execAsync = promisify(exec)
1696
const PRESET_COLORS = '#3B89C7,#C98D09,#1BBE4D,#D8D8D4,#774399,#FF7896,#F9F871'.split(',')
1797

98+
export async function getStorage() {
99+
const { stdout: dfOutput } = await execAsync('df /pibox')
100+
const lines = dfOutput.split('\n')
101+
const data = lines[1].split(/\s+/)
102+
const usedSpace = data[2]
103+
const totalSpace = data[1]
104+
const percentageUsed = parseInt(data[4], 10)
105+
return { usedSpace, totalSpace, percentageUsed }
106+
}
107+
18108
export async function drawHomeScreen() {
19109
const start = performance.now()
20110
const width = 240
@@ -24,13 +114,11 @@ export async function drawHomeScreen() {
24114
const ctx = canvas.getContext('2d')
25115

26116
if (!global.storage || global.storage.lastChecked < Date.now() - 1000 * 60 * 1) {
27-
const { stdout: dfOutput } = await execAsync('df /pibox')
28-
const lines = dfOutput.split('\n')
29-
const data = lines[1].split(/\s+/)
117+
const { usedSpace, totalSpace, percentageUsed } = await getStorage()
30118
global.storage = {}
31-
global.storage.usedSpace = data[2]
32-
global.storage.totalSpace = data[1]
33-
global.storage.percentageUsed = parseInt(data[4], 10)
119+
global.storage.usedSpace = usedSpace
120+
global.storage.totalSpace = totalSpace
121+
global.storage.percentageUsed = percentageUsed
34122
global.storage.lastChecked = Date.now()
35123
}
36124

src/pages/api/login.js

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import { readFile } from 'fs/promises'
22
import { setTimeout as setTimeoutPromise } from 'timers/promises'
3-
import {
4-
startHomeScreen,
5-
getOwner,
6-
getConfig,
7-
saveConfig,
8-
checkSystemPassword,
9-
execAsync,
10-
sha256HexDigest,
11-
execAndLog,
12-
} from '@/functions'
3+
import { startHomeScreen, getOwner, getConfig, saveConfig, saveSambaConfig, ensureTimeMachine, checkSystemPassword, execAsync, sha256HexDigest, execAndLog } from '@/functions'
134
import c from 'chalk'
145

156
const OTP_EXPIRY_WINDOW = 1000 * 60 * 10 // 10 minutes
@@ -105,6 +96,8 @@ export default async function handler(req, res) {
10596
await setTimeoutPromise(3000)
10697
global.ALL_DISKS_UNLOCKED = true
10798
startHomeScreen()
99+
saveSambaConfig()
100+
ensureTimeMachine()
108101
}
109102

110103
await pushSession({ sessionKey, sessionName, sessionPlatform, user })

src/pages/api/permissions.js

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { middlewareAuth, execAndLog } from '@/functions'
2-
import { getConfig, saveConfig, sha256HexDigest } from '@/functions'
2+
import { getConfig, saveConfig, saveSambaConfig, sha256HexDigest } from '@/functions'
33
import { stat, writeFile } from 'fs/promises'
44
import { PIBOX_FILES_PREFIX } from '@/constants'
55
import { createDiffieHellmanGroup } from 'crypto'
@@ -75,27 +75,8 @@ export default async function handler(req, res) {
7575
config.groups.push({ groupName: share.groupName, users: users })
7676
}
7777

78-
let smbConfig = `# Group Name to User mapping`
79-
smbConfig += config.groups.map((group) => `# ${group.groupName} => ${group.usersString}\n`)
80-
smbConfig += `\n\n`
81-
smbConfig = `[global]
82-
netbios name = PIBOX
83-
workgroup = WORKGROUP
84-
access based share enum = yes
85-
logging = syslog
86-
server role = standalone server
87-
veto files = /._*/.DS_Store/
88-
delete veto files = yes\n\n`
89-
90-
config.shares.forEach((share) => {
91-
smbConfig += `[${share.name}]
92-
path = ${PIBOX_FILES_PREFIX + share.path}
93-
read only = no
94-
valid users = @${share.groupName}\n\n`
95-
})
96-
97-
await saveConfig(config)
98-
await writeFile('/etc/samba/smb.conf', smbConfig)
78+
await saveConfig()
79+
await saveSambaConfig()
9980
await execAndLog('global:samba', 'systemctl restart smbd')
10081
res.status(200).json({ message: 'Permissions updated' })
10182
}

src/pages/api/setup.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
setSambaPassword,
1111
startHomeScreen,
1212
writeScreen,
13+
saveSambaConfig,
14+
ensureTimeMachine,
1315
} from '@/functions'
1416
import { stat, mkdir, writeFile, unlink } from 'fs/promises'
1517
import { PIBOX_UNENCRYPTED_CONFIG_DIR, SETUP_COMPLETE_CHECK_FILEPATH, UPDATE_IN_PROGRESS_CHECK_FILEPATH } from '@/constants'
@@ -176,7 +178,10 @@ async function initialSetup(req, res) {
176178

177179
await saveOwner(username)
178180
await saveConfig(config)
179-
execAsync('sync')
181+
182+
saveSambaConfig()
183+
ensureTimeMachine()
184+
execAsync('sync') //flush changes to disk
180185
startHomeScreen()
181186
res.status(200).json({ success: true })
182187
}

src/pages/index.js

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ export default function Home() {
1010
const [loading, setLoading] = useState(true)
1111
const [status, setStatus] = useState(null)
1212
const [mac, setMac] = useState(false)
13-
const [whoami, setWhoami] = useState({
14-
linuxUser: '<username>',
15-
})
13+
const [whoami, setWhoami] = useState(null)
1614

1715
async function getStatus() {
1816
if (typeof window === 'undefined') return
@@ -28,7 +26,9 @@ export default function Home() {
2826
async function getWhoami() {
2927
if (typeof window === 'undefined') return
3028
const { body: whoami } = await fetchApi('/api/whoami')
31-
setWhoami(whoami)
29+
if (!whoami.error) {
30+
setWhoami(whoami)
31+
}
3232
}
3333

3434
useEffect(() => {
@@ -44,62 +44,72 @@ export default function Home() {
4444
{loading ? (
4545
<p>Loading...</p>
4646
) : status?.unlocked ? (
47-
<div>
48-
<p>
49-
To access your files from{' '}
50-
<a className={`${mac ? 'font-semibold' : 'text-blue-500 cursor-pointer '}`} onClick={() => setMac(true)}>
51-
Mac
52-
</a>{' '}
53-
/{' '}
54-
<a className={`${!mac ? 'font-semibold' : 'text-blue-500 cursor-pointer '}`} onClick={() => setMac(false)}>
55-
PC
56-
</a>
57-
:
58-
</p>
59-
{mac ? (
60-
<ol className="list-decimal list-inside pl-4 mt-4">
61-
<li>Open Finder → Locations → Network</li>
62-
<li>Open PIBOX</li>
63-
<li>Click the &quot;Connect As...&quot; button</li>
64-
<ol className="list-disc list-inside ml-6">
65-
<li>Select &quot;Registered User&quot;</li>
47+
whoami ? (
48+
<div>
49+
<p>
50+
To access your files from{' '}
51+
<a className={`${mac ? 'font-semibold' : 'text-blue-500 cursor-pointer '}`} onClick={() => setMac(true)}>
52+
Mac
53+
</a>{' '}
54+
/{' '}
55+
<a className={`${!mac ? 'font-semibold' : 'text-blue-500 cursor-pointer '}`} onClick={() => setMac(false)}>
56+
PC
57+
</a>
58+
:
59+
</p>
60+
{mac ? (
61+
<ol className="list-decimal list-inside pl-4 mt-4">
62+
<li>Open Finder → Locations → Network</li>
63+
<li>Open PIBOX</li>
64+
<li>Click the &quot;Connect As...&quot; button</li>
65+
<ol className="list-disc list-inside ml-6">
66+
<li>Select &quot;Registered User&quot;</li>
67+
<li>
68+
Name: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">{whoami.linuxUser}</span>
69+
</li>
70+
<li>
71+
Password: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">&lt;your password&gt;</span>
72+
</li>
73+
</ol>
74+
</ol>
75+
) : (
76+
<ol className="list-decimal list-inside pl-4 mt-4">
77+
<li>Open File Explorer → This PC</li>
78+
<li>Right Click → Add Network Location</li>
6679
<li>
67-
Name: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">{whoami.linuxUser}</span>
80+
Click <b className="font-semibold">Next</b> → Custom Network Location → <b className="font-semibold">Next</b>
6881
</li>
6982
<li>
70-
Password: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">&lt;your password&gt;</span>
83+
Enter <b className="font-semibold">\\pibox.local</b>
84+
and click <b className="font-semibold">Browse</b>
7185
</li>
72-
</ol>
73-
</ol>
74-
) : (
75-
<ol className="list-decimal list-inside pl-4 mt-4">
76-
<li>Open File Explorer → This PC</li>
77-
<li>Right Click → Add Network Location</li>
78-
<li>
79-
Click <b className="font-semibold">Next</b> → Custom Network Location → <b className="font-semibold">Next</b>
80-
</li>
81-
<li>
82-
Enter <b className="font-semibold">\\pibox.local</b>
83-
and click <b className="font-semibold">Browse</b>
84-
</li>
85-
<li>Expand &quot;pibox.local&quot;</li>
86-
<li>A dialog titled &quot;Enter network credentials&quot; appears</li>
87-
<ol className="list-disc list-inside ml-6">
88-
<li>Select &quot;Registered User&quot;</li>
86+
<li>Expand &quot;pibox.local&quot;</li>
87+
<li>A dialog titled &quot;Enter network credentials&quot; appears</li>
88+
<ol className="list-disc list-inside ml-6">
89+
<li>Select &quot;Registered User&quot;</li>
90+
<li>
91+
User name: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">{whoami.linuxUser}</span>
92+
</li>
93+
<li>
94+
Password: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">&lt;your password&gt;</span>
95+
</li>
96+
</ol>
97+
<li>Select the folder want to map</li>
8998
<li>
90-
User name: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">{whoami.linuxUser}</span>
91-
</li>
92-
<li>
93-
Password: <span className="text-slate-600 bg-slate-200 border rounded border-slate-400 px-1 py-0.5 text-sm">&lt;your password&gt;</span>
99+
Click <b className="font-semibold">Ok</b><b className="font-semibold">Next</b><b className="font-semibold">Finish</b>
94100
</li>
95101
</ol>
96-
<li>Select the folder want to map</li>
97-
<li>
98-
Click <b className="font-semibold">Ok</b><b className="font-semibold">Next</b><b className="font-semibold">Finish</b>
99-
</li>
100-
</ol>
101-
)}
102-
</div>
102+
)}
103+
</div>
104+
) : (
105+
<div>
106+
Please{' '}
107+
<Link className="text-steel-blue-500 hover:underline" href="/login">
108+
Login
109+
</Link>
110+
.
111+
</div>
112+
)
103113
) : (
104114
<div>
105115
<div className="rounded bg-steel-blue-300 px-4 py-2 font-bold">

src/pages/setup.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,15 @@ export default function Home() {
6363
setLoading(false)
6464
setDisks(jsonData)
6565
}
66-
fetchDisks()
66+
async function fetchStatus() {
67+
const { body } = await fetchApi('/api/status')
68+
if (body.setupComplete) {
69+
window.location.href = '/'
70+
} else {
71+
fetchDisks()
72+
}
73+
}
74+
fetchStatus()
6775
}, [])
6876

6977
return (
@@ -191,7 +199,7 @@ export default function Home() {
191199
{setupLoading ? (
192200
<div className="flex justify-center items-center mt-12">
193201
<FontAwesomeIcon icon={faCircleNotch} className="fa-spin mr-2" />
194-
Saving your settings. This may take a few seconds...
202+
Setting up your PiBox. This may take a few seconds...
195203
</div>
196204
) : (
197205
<div className="flex justify-between mt-4">

0 commit comments

Comments
 (0)