diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5e7c7b3..656860f 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -2,8 +2,9 @@ name: CI
on:
push:
- branches: ["**"]
+ branches: ["main"]
pull_request:
+ branches: ["main"]
jobs:
secrets:
diff --git a/README.md b/README.md
index 2f7f4fb..4e69e84 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ To learn more about the war and how you can help, [click here](https://war.ukrai
- Browse Secrets, Keys, Certificates
- Secret metadata + explicit value fetch flow
- Secret CRUD lifecycle (set/delete/recover/purge)
+- Import secrets from JSON file
- Bulk delete safety flow:
- typed confirmation (`delete`)
- collapsible list of selected secrets
@@ -118,6 +119,46 @@ Optional mock mode for UI development:
VITE_ENABLE_MOCK_MODE=true npm run dev
```
+## Import Secrets from JSON
+
+Use the **Import JSON** button in the Secrets toolbar, or run **Import Secrets from JSON** from the command palette.
+
+Accepted formats:
+
+```json
+[
+ {
+ "name": "my-secret",
+ "value": "secret-value",
+ "contentType": "text/plain",
+ "enabled": true,
+ "expires": "2030-01-01T00:00:00Z",
+ "notBefore": "2026-01-01T00:00:00Z",
+ "tags": {
+ "env": "prod"
+ }
+ }
+]
+```
+
+or:
+
+```json
+{
+ "secrets": [
+ { "name": "my-secret", "value": "secret-value" }
+ ]
+}
+```
+
+Rules:
+- `name` and `value` are required.
+- `name` supports letters, numbers, and dashes only.
+- `tags` must be an object with string values.
+- `expires` and `notBefore` must be valid date strings.
+
+Sample file: [`examples/secrets-import.example.json`](./examples/secrets-import.example.json)
+
## Quality Gates
Frontend:
diff --git a/examples/secrets-import.example.json b/examples/secrets-import.example.json
new file mode 100644
index 0000000..2b66b06
--- /dev/null
+++ b/examples/secrets-import.example.json
@@ -0,0 +1,21 @@
+{
+ "secrets": [
+ {
+ "name": "api-key-prod",
+ "value": "replace-me",
+ "contentType": "text/plain",
+ "enabled": true,
+ "tags": {
+ "env": "prod",
+ "team": "platform"
+ }
+ },
+ {
+ "name": "app-config-json",
+ "value": "{\"featureFlag\":true}",
+ "contentType": "application/json",
+ "expires": "2030-01-01T00:00:00Z",
+ "notBefore": "2026-01-01T00:00:00Z"
+ }
+ ]
+}
diff --git a/src/components/auth/SignIn.tsx b/src/components/auth/SignIn.tsx
index eb7e2b2..16fac73 100644
--- a/src/components/auth/SignIn.tsx
+++ b/src/components/auth/SignIn.tsx
@@ -4,10 +4,10 @@ import {
Card,
CardFooter,
CardHeader,
+ makeStyles,
Spinner,
Text,
Tooltip,
- makeStyles,
tokens,
} from '@fluentui/react-components';
import {
@@ -243,9 +243,7 @@ export function SignIn() {
- }
+ image={}
header={
@@ -338,9 +336,7 @@ export function SignIn() {
/>
-
- Then click Connect below to verify the session.
-
+ Then click Connect below to verify the session.
{/* Error */}
diff --git a/src/components/certificates/CertificateDetails.tsx b/src/components/certificates/CertificateDetails.tsx
index ee5874d..623010f 100644
--- a/src/components/certificates/CertificateDetails.tsx
+++ b/src/components/certificates/CertificateDetails.tsx
@@ -2,9 +2,9 @@ import {
Badge,
Button,
Field,
- Text,
makeStyles,
mergeClasses,
+ Text,
tokens,
} from '@fluentui/react-components';
import { Certificate24Regular, Copy24Regular, Dismiss24Regular } from '@fluentui/react-icons';
diff --git a/src/components/certificates/CertificatesList.tsx b/src/components/certificates/CertificatesList.tsx
index c2b4d82..01b7526 100644
--- a/src/components/certificates/CertificatesList.tsx
+++ b/src/components/certificates/CertificatesList.tsx
@@ -1,11 +1,4 @@
-import {
- Button,
- Input,
- Text,
- makeStyles,
- mergeClasses,
- tokens,
-} from '@fluentui/react-components';
+import { Button, Input, makeStyles, mergeClasses, Text, tokens } from '@fluentui/react-components';
import { Search24Regular } from '@fluentui/react-icons';
import { useQuery } from '@tanstack/react-query';
import { useMemo, useState } from 'react';
@@ -161,10 +154,7 @@ export function CertificatesList() {
);
const expired = new Date(item.expires) < new Date();
return (
-
+
{renderDate(item.expires)}
);
diff --git a/src/components/command-palette/CommandPalette.tsx b/src/components/command-palette/CommandPalette.tsx
index b26d5e3..6fb51e1 100644
--- a/src/components/command-palette/CommandPalette.tsx
+++ b/src/components/command-palette/CommandPalette.tsx
@@ -1,4 +1,4 @@
-import { Input, Text, makeStyles, tokens } from '@fluentui/react-components';
+import { Input, makeStyles, Text, tokens } from '@fluentui/react-components';
import { Search24Regular } from '@fluentui/react-icons';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useAppStore } from '../../stores/appStore';
@@ -133,11 +133,7 @@ export function CommandPalette() {
onClick={() => execute(result.item)}
onMouseEnter={() => setActiveIndex(i)}
>
- {result.item.icon && (
-
- {result.item.icon}
-
- )}
+ {result.item.icon && {result.item.icon}}
{result.item.label}
@@ -245,6 +241,13 @@ function useCommands(): PaletteCommand[] {
execute: () => window.dispatchEvent(new CustomEvent('azv:new-secret')),
when: () => !!store.selectedVaultName,
},
+ {
+ id: 'import-secrets-json',
+ label: 'Import Secrets from JSON',
+ category: 'action',
+ execute: () => window.dispatchEvent(new CustomEvent('azv:import-secrets')),
+ when: () => !!store.selectedVaultName,
+ },
{
id: 'select-all',
label: 'Select All Items',
diff --git a/src/components/common/DangerConfirmDialog.tsx b/src/components/common/DangerConfirmDialog.tsx
index 3ea2771..399a0a7 100644
--- a/src/components/common/DangerConfirmDialog.tsx
+++ b/src/components/common/DangerConfirmDialog.tsx
@@ -7,9 +7,9 @@ import {
DialogSurface,
DialogTitle,
Input,
+ makeStyles,
Spinner,
Text,
- makeStyles,
tokens,
} from '@fluentui/react-components';
import { Warning24Regular } from '@fluentui/react-icons';
@@ -99,9 +99,7 @@ export function DangerConfirmDialog({
{isCritical && (
-
- This action is irreversible.
-
+ This action is irreversible.
)}
diff --git a/src/components/common/EmptyState.tsx b/src/components/common/EmptyState.tsx
index 482cf82..9a8f7d9 100644
--- a/src/components/common/EmptyState.tsx
+++ b/src/components/common/EmptyState.tsx
@@ -1,4 +1,4 @@
-import { Button, Text, makeStyles, tokens } from '@fluentui/react-components';
+import { Button, makeStyles, Text, tokens } from '@fluentui/react-components';
interface EmptyStateProps {
icon?: React.ReactNode;
@@ -37,7 +37,12 @@ export function EmptyState({ icon, title, description, action }: EmptyStateProps
)}
{action && (
-