Quasi finito

This commit is contained in:
2022-07-04 20:08:00 +02:00
parent 0c2009b659
commit bd0a9b5c6e
8 changed files with 463 additions and 61 deletions
+4
View File
@@ -7,6 +7,8 @@ import { AdminPanel } from '../pages/Admin.jsx'
import { ApiKeys } from '../pages/ApiKeys.jsx'
import { Login } from '../pages/Login.jsx'
import { Dashboard } from '../pages/Dashboard.jsx'
import { Buckets } from '../pages/Buckets.jsx'
import { Bucket } from '../pages/Bucket.jsx'
export const MainContent = () => {
const { setSidebarVisible, route, user } = useStore()
@@ -17,6 +19,8 @@ export const MainContent = () => {
login: Login,
admin: AdminPanel,
apiKeys: ApiKeys,
buckets: Buckets,
bucket: Bucket,
}[route.id] || (() => <>Still nothing here</>)
return (
+4
View File
@@ -39,6 +39,10 @@ const App = () => {
},
})
if (id === 'unknown') {
location.href = '/'
}
const user = useUser(user => {
if (id !== 'login' && user !== 'admin') {
location.href = '/login'
+106
View File
@@ -0,0 +1,106 @@
import { useEffect, useRef, useState } from 'preact/hooks'
import { Icon } from '../components/Icon.jsx'
import { useStore } from '../context.jsx'
import { useRemoteState } from '../util.jsx'
export const Bucket = () => {
const {
route: {
params: { bucket },
},
showToast,
} = useStore()
const [bucketObjects, updateBucketObjects] = useRemoteState(`/api/buckets/${bucket}`, [])
const fileInputRef = useRef(null)
const uploadFile = async () => {
const formData = new FormData()
formData.append('file', fileInputRef.current.files[0])
showToast(<>Sto caricando il file...</>)
const res = await fetch(`/api/buckets/${bucket}`, {
method: 'POST',
body: formData,
})
if (res.ok) {
const { id } = await res.json()
showToast(
<>
File caricato con id <code>{id}</code>
</>
)
await updateBucketObjects()
}
}
const deleteObject = async id => {
const res = await fetch(`/api/buckets/${bucket}/${id}`, { method: 'DELETE' })
if (res.ok) {
showToast('Oggetto rimosso')
} else {
showToast(`Errore: ${await res.text()}`)
}
}
return (
<>
<div class="panel">
<div class="row">
<div class="row-group">
<div class="title">
Bucket / <code>{bucket}</code>
</div>
</div>
<div class="row-group">
<button class="icon" onClick={() => fileInputRef.current.click()}>
<Icon name="file_upload" />
</button>
<input
type="file"
ref={fileInputRef}
onChange={() => uploadFile()}
style="display: none;"
/>
</div>
</div>
<div class="table">
<div class="cells">
{/* Header */}
<div class="header">Objects</div>
<div class="header">Actions</div>
{/* Rows */}
{bucketObjects.map(key => (
<>
<div class="name">
<code>{key}</code>
</div>
<div class="options">
<a
class="button icon flat"
href={`/api/buckets/${bucket}/${key}`}
>
<Icon name="file_download" />
</a>
<button
class="icon delete flat"
onClick={() => deleteObject(key)}
>
<Icon name="delete" />
</button>
</div>
</>
))}
</div>
</div>
</div>
</>
)
}
+118
View File
@@ -0,0 +1,118 @@
import { useEffect, useState } from 'preact/hooks'
import { Icon } from '../components/Icon.jsx'
import { showToast } from '../components/Toasts.jsx'
import { useRemoteState } from '../util.jsx'
export const Buckets = () => {
const [buckets, updateBuckets] = useRemoteState('/api/buckets', [])
const [allBucketSettings, setAllBucketSettings] = useState({})
const updateBucketSettings = async bucket => {
const res = await fetch(`/api/buckets/${bucket}/settings`)
const value = await res.json()
setAllBucketSettings(s => ({ ...s, [bucket]: value }))
}
useEffect(() => {
for (const bucket of buckets) {
updateBucketSettings(bucket)
}
}, [buckets])
const [newBucketName, setNewBucketName] = useState('')
const [newBucketSettings, setNewBucketSettings] = useState('')
const createBucket = async () => {
const { path } = JSON.parse(newBucketSettings || '{"path":""}')
const res = await fetch(`/api/buckets/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
bucket: newBucketName,
path,
}),
})
if (res.ok) {
await updateBuckets()
} else {
showToast(`Errore: ${await res.text()}`)
}
}
return (
<>
<div class="panel">
<div class="row">
<div class="row-group">
<div class="title">Buckets</div>
</div>
</div>
<div class="table">
<div class="cells">
{/* Header */}
<div class="header">Bucket</div>
<div class="header">Settings</div>
<div class="header">Options</div>
{/* Rows */}
{buckets.map(bucket => (
<>
<div class="name">
<a href={`/buckets/${bucket}`}>
<code>{bucket}</code>
</a>
</div>
<div class="settings">
<input
readonly
class="mono"
type="text"
value={JSON.stringify(allBucketSettings[bucket])}
/>
</div>
<div class="options">
<button class="icon delete flat">
<Icon name="delete" />
</button>
</div>
</>
))}
</div>
</div>
</div>
<form action="/api/buckets" method="POST">
<div class="fill title">Nuovo Bucket</div>
<label for="new-bucket-name">Name</label>
<input
type="text"
name="bucket"
id="new-bucket-name"
placeholder="new-bucket"
value={newBucketName}
onInput={e => setNewBucketName(e.target.value)}
/>
<label for="new-bucket-settings">Settings</label>
<input
type="text"
name="settings"
id="new-bucket-settings"
placeholder={`{"path":"${newBucketName || 'new-bucket'}/"}`}
value={newBucketSettings}
onInput={e => setNewBucketSettings(e.target.value)}
/>
<div class="fill center">
<button type="button" onClick={() => createBucket()}>
Crea Bucket
</button>
</div>
</form>
</>
)
}
+28 -1
View File
@@ -33,8 +33,14 @@ export function useUser(onLoginStateChanged) {
return user
}
//
// Hooks
//
// Router
function matchPattern(pattern, url) {
const r = `^${pattern.replace(/:([a-zA-Z0-9\_\-]+)/g, '(?<$1>.+?)')}$`
const r = `^${pattern.replace(/:([a-zA-Z0-9\_\-]+)/g, '(?<$1>[^\\/\\?]+?)')}$`
return new RegExp(r).exec(url)
}
@@ -49,6 +55,27 @@ export const useRouter = routes => {
return ['unknown', { id: 'unknown' }, {}]
}
// useRemoteState
export function useRemoteState(url, initialValue = null) {
const [value, setValue] = useState(initialValue)
const updateValue = async () => {
const res = await fetch(url)
if (!res.ok) {
console.error(res.body)
}
setValue(await res.json())
}
useEffect(() => {
updateValue()
}, [])
return [value, updateValue]
}
//
// Change Case Utility
//