3.14 Password Vault encryption AES-256-GCM server-side
- Created src/core/crypto/ with AES-256-GCM encrypt/decrypt (PBKDF2 key derivation) - Created /api/vault route: CRUD with server-side password encryption - PATCH /api/vault migration endpoint to re-encrypt legacy plaintext passwords - Rewrote use-vault hook to use dedicated /api/vault instead of generic storage - Updated UI: amber 'not encrypted' warning green 'encrypted' badge - Added ENCRYPTION_SECRET env var to docker-compose.yml and stack.env - Module version bumped to 0.2.0
This commit is contained in:
@@ -122,8 +122,9 @@ export function WordTemplatesModule() {
|
||||
null,
|
||||
);
|
||||
const [deletingId, setDeletingId] = useState<string | null>(null);
|
||||
const [revisingTemplate, setRevisingTemplate] =
|
||||
useState<WordTemplate | null>(null);
|
||||
const [revisingTemplate, setRevisingTemplate] = useState<WordTemplate | null>(
|
||||
null,
|
||||
);
|
||||
const [revisionUrl, setRevisionUrl] = useState("");
|
||||
const [revisionNotes, setRevisionNotes] = useState("");
|
||||
const [historyTemplate, setHistoryTemplate] = useState<WordTemplate | null>(
|
||||
@@ -172,7 +173,12 @@ export function WordTemplatesModule() {
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Word/Excel</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{allTemplates.filter((t) => (t.fileType ?? "docx") === "docx" || t.fileType === "xlsx").length}
|
||||
{
|
||||
allTemplates.filter(
|
||||
(t) =>
|
||||
(t.fileType ?? "docx") === "docx" || t.fileType === "xlsx",
|
||||
).length
|
||||
}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -180,7 +186,11 @@ export function WordTemplatesModule() {
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">DWG/Archicad</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{allTemplates.filter((t) => t.fileType === "dwg" || t.fileType === "archicad").length}
|
||||
{
|
||||
allTemplates.filter(
|
||||
(t) => t.fileType === "dwg" || t.fileType === "archicad",
|
||||
).length
|
||||
}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -188,7 +198,10 @@ export function WordTemplatesModule() {
|
||||
<CardContent className="p-4">
|
||||
<p className="text-xs text-muted-foreground">Cu versiuni</p>
|
||||
<p className="text-2xl font-bold">
|
||||
{allTemplates.filter((t) => (t.versionHistory ?? []).length > 0).length}
|
||||
{
|
||||
allTemplates.filter((t) => (t.versionHistory ?? []).length > 0)
|
||||
.length
|
||||
}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -431,9 +444,7 @@ export function WordTemplatesModule() {
|
||||
>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
Revizie nouă — {revisingTemplate?.name}
|
||||
</DialogTitle>
|
||||
<DialogTitle>Revizie nouă — {revisingTemplate?.name}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -546,10 +557,7 @@ export function WordTemplatesModule() {
|
||||
)}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setHistoryTemplate(null)}
|
||||
>
|
||||
<Button variant="outline" onClick={() => setHistoryTemplate(null)}>
|
||||
Închide
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
@@ -726,13 +734,13 @@ function TemplateForm({
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{(
|
||||
Object.keys(FILE_TYPE_LABELS) as TemplateFileType[]
|
||||
).map((ft) => (
|
||||
<SelectItem key={ft} value={ft}>
|
||||
{FILE_TYPE_LABELS[ft]}
|
||||
</SelectItem>
|
||||
))}
|
||||
{(Object.keys(FILE_TYPE_LABELS) as TemplateFileType[]).map(
|
||||
(ft) => (
|
||||
<SelectItem key={ft} value={ft}>
|
||||
{FILE_TYPE_LABELS[ft]}
|
||||
</SelectItem>
|
||||
),
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
@@ -112,7 +112,8 @@ export function useTemplates() {
|
||||
updatedAt: now,
|
||||
};
|
||||
if (notes) {
|
||||
updated.versionHistory[updated.versionHistory.length - 1]!.notes = notes;
|
||||
updated.versionHistory[updated.versionHistory.length - 1]!.notes =
|
||||
notes;
|
||||
}
|
||||
await storage.set(`${PREFIX}${id}`, updated);
|
||||
await refresh();
|
||||
|
||||
Reference in New Issue
Block a user