This commit is contained in:
Louis Mazin 2026-10-14 01:29:42 +02:00
parent df47775b0a
commit f3a7cf6337
9 changed files with 92 additions and 11 deletions

View File

@ -0,0 +1,53 @@
"""Normalize fresh installations to the physical storage root.
Older databases with existing /root content MUST be converted explicitly using
`manage.py flatten_drive_root --apply`, after a backup. This migration does not
move, delete or rewrite any user file.
"""
from pathlib import Path
from django.conf import settings
from django.db import migrations, transaction
from django.db.models import Q
def normalize_unused_root(apps, schema_editor):
Space = apps.get_model('drive', 'DriveSpace')
Item = apps.get_model('drive', 'DriveItem')
Upload = apps.get_model('drive', 'UploadSession')
root = Space.objects.filter(parent__isnull=True, private_owner__isnull=True).order_by('pk').first()
if root is None or root.relative_path != 'root':
return
# The previous migrations create a logical "root" even for an empty DB.
# Existing installations must not be migrated behind the user's back:
# physical bytes must be moved consistently with DB paths.
old_dir = Path(settings.DRIVE_STORAGE_ROOT).resolve() / 'root'
if (old_dir.exists() and (not old_dir.is_dir() or any(old_dir.iterdir()))):
print('GoodDrive: root/ has data; run flatten_drive_root --apply after backup.')
return
if (Item.objects.filter(storage_location__startswith='root/').exists()
or Upload.objects.exists()):
print('GoodDrive: existing files/uploads; run flatten_drive_root --apply after backup.')
return
old = list(Space.objects.filter(Q(pk=root.pk) | Q(relative_path__startswith='root/')))
new = {s.pk: ('' if s.pk == root.pk else s.relative_path[5:]) for s in old}
if any(not path or path.startswith('/') for pk, path in new.items() if pk != root.pk):
print('GoodDrive: invalid old space paths; use flatten_drive_root after inspection.')
return
other_paths = set(Space.objects.exclude(pk__in=new).values_list('relative_path', flat=True))
if any(path in other_paths for path in new.values()):
print('GoodDrive: space paths conflict; use flatten_drive_root after inspection.')
return
with transaction.atomic():
for space in old:
space.relative_path = new[space.pk]
Space.objects.bulk_update(old, ['relative_path'])
print('GoodDrive: new database root uses DRIVE_STORAGE_ROOT directly (no /root/).')
class Migration(migrations.Migration):
dependencies = [('drive', '0009_managed_spaces')]
operations = [migrations.RunPython(normalize_unused_root, migrations.RunPython.noop)]

View File

@ -0,0 +1,14 @@
# Generated by Django 5.2.18 on 2026-10-13 23:29
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('drive', '0010_merge_0008_alter_spacegrant_id_0009_managed_spaces'),
('drive', '0011_normalize_empty_root'),
]
operations = [
]

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon.svg" />
<link rel="icon" type="image/png" href="/icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GooDrive</title>
</head>

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="17" fill="#455ea3"/><path d="M19 20h11l6 6h11a5 5 0 0 1 5 5v15a5 5 0 0 1-5 5H17a5 5 0 0 1-5-5V25a5 5 0 0 1 7-5Z" fill="#fff" opacity=".92"/><path d="M12 32h40" stroke="#455ea3" stroke-width="3"/></svg>

Before

Width:  |  Height:  |  Size: 295 B

BIN
frontend/public/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react'
import { Button } from '@/components/ui/button'
import ContextActions, {spaceOptions, mobileDots} from './ContextActions'
import { MoreHorizontal, Plus } from 'lucide-react'
import { Database, FolderPlus, HardDrive, LogOut, FolderLock, FolderOpen, X } from 'lucide-react'
import { Database, FolderPlus, LogOut, FolderLock, FolderOpen, X } from 'lucide-react'
import { sections } from '@/config/navigation'
import { fmtSize } from '@/lib/drive-format'
@ -58,10 +58,14 @@ export default function DriveSidebar({ controller, user, onLogout }) {
<aside className="drive-sidebar flex flex-col" data-open={sidebar}>
<div className="flex h-20 items-center gap-3 px-5">
<span className="drive-brand flex size-11 items-center justify-center rounded-2xl text-white shadow-sm">
<HardDrive className="size-6" />
<img
src="/icon.png"
alt="Logo de GooDrive"
className="block size-full object-contain"
/>
</span>
<div>
<div className="text-lg font-bold tracking-tight">GoodDrive</div>
<div className="text-lg font-bold tracking-tight">GooDrive</div>
<div className="text-xs text-muted-foreground">Un Drive qui se partage</div>
</div>
</div>

View File

@ -1,6 +1,5 @@
import { Button } from '@/components/ui/button'
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Trash2 } from 'lucide-react'
export default function DeleteDialog({controller: c}) {
const {modal, setModal, action, busy, handleAction} = c

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { ArrowRight, HardDrive, LoaderCircle } from 'lucide-react'
import { ArrowRight, LoaderCircle } from 'lucide-react'
import { drive, message } from '@/api/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
@ -35,7 +35,11 @@ export default function InvitationPage({ token }) {
return <div className="flex min-h-dvh items-center justify-center bg-background p-4">
<div className="w-full max-w-lg space-y-6 rounded-3xl border bg-card p-7 shadow-xl sm:p-10">
<div className="flex items-center gap-3"><div className="drive-brand grid size-12 place-items-center rounded-2xl text-white"><HardDrive /></div><div><h1 className="text-xl font-bold">GoodDrive</h1><p className="text-xs text-muted-foreground">Un espace pour ceux qui en ont besoin.</p></div></div>
<div className="flex items-center gap-3"><div className="drive-brand grid size-12 place-items-center rounded-2xl text-white"><img
src="/icon.png"
alt="Logo de GooDrive"
className="block size-full object-contain"
/></div><div><h1 className="text-xl font-bold">GoodDrive</h1><p className="text-xs text-muted-foreground">Un espace pour ceux qui en ont besoin.</p></div></div>
{done ? <div className="space-y-4"><h2 className="text-2xl font-semibold">Bienvenue sur GoodDrive !</h2><p>Ton espace personnel et ton espace partagé sont prêts. Tu peux maintenant te connecter.</p><Button onClick={() => window.location.assign('/')} className="w-full">Se connecter <ArrowRight /></Button></div>
: info ? <><div><h2 className="text-2xl font-semibold">Créer mon espace</h2><p className="mt-2 text-sm text-muted-foreground">Tu es invité dans l'arborescence de « {info.parent_name} ». Tu disposeras de « Mes fichiers », privé, et d’un accès à l’espace partagé attribué.</p>{info.quota_bytes != null && <p className="mt-2 text-sm font-medium">Espace attribué : {fmtSize(info.quota_bytes)}</p>}</div>
<form onSubmit={submit} className="space-y-3">

View File

@ -2,7 +2,7 @@ import { useState } from 'react'
import { api, logoutTokens, message, storeTokens } from '@/api/client'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { ArrowUpRight, HardDrive, LoaderCircle, ShieldCheck } from 'lucide-react'
import { ArrowUpRight, LoaderCircle, ShieldCheck } from 'lucide-react'
export default function LoginPage({ onSigned }) {
const [username, setUsername] = useState('')
@ -35,7 +35,11 @@ export default function LoginPage({ onSigned }) {
<div className="absolute -left-28 bottom-16 size-72 rounded-full bg-white/5" />
<div className="relative flex items-center gap-3">
<div className="flex size-11 items-center justify-center rounded-2xl bg-white/15">
<HardDrive className="size-6" />
<img
src="/icon.png"
alt="Logo de GooDrive"
className="block size-full object-contain"
/>
</div>
<span className="text-xl font-semibold tracking-tight">GoodDrive</span>
</div>
@ -54,7 +58,11 @@ export default function LoginPage({ onSigned }) {
<div className="flex min-h-[480px] flex-col justify-center p-7 sm:p-12">
<div className="mb-8 flex items-center gap-3 md:hidden">
<span className="drive-brand flex size-10 items-center justify-center rounded-xl text-white">
<HardDrive />
<img
src="/icon.png"
alt="Logo de GooDrive"
className="block size-full object-contain"
/>
</span>
<b className="text-xl">GoodDrive</b>
</div>