Storing Frontend Data
By default, the frontend component of a plugin is stateless, meaning data will be lost between Caido sessions. However, if your plugin needs to persist data in the frontend you can utilize the storage system through sdk.storage
.
The storage system is defined by the StorageSDK interface and provides these methods:
set()
: Puts new data into the storage.get()
: Fetches the current data from storage.onChange()
: Sets up a listener that runs when the storage changes.
User Preferences
To demonstrate its usage, let's create a frontend interface that offers light and dark theme options.
App.vue
<script setup lang="ts">
import Button from "primevue/button";
import { useSDK } from "@/plugins/sdk";
import { ref, onMounted, watch } from "vue";
const sdk = useSDK();
const currentTheme = ref<"light" | "dark" | null>(null);
const updateTheme = async (theme: "light" | "dark") => {
await sdk.storage.set({ theme });
currentTheme.value = theme;
};
const applyTheme = (theme: "light" | "dark") => {
const root = document.getElementById('plugin--frontend-vue');
if (root) {
if (theme === "dark") {
root.style.backgroundColor = "#202227";
} else {
root.style.backgroundColor = "#FFFFFF";
}
}
};
const loadSettings = () => {
const settings = sdk.storage.get() as { theme: "light" | "dark" } | null;
if (settings?.theme) {
currentTheme.value = settings.theme;
applyTheme(settings.theme);
} else {
currentTheme.value = "light";
applyTheme("light");
}
};
watch(currentTheme, (newTheme) => {
if (newTheme) {
applyTheme(newTheme);
}
});
sdk.storage.onChange((newSettings) => {
const settings = newSettings as { theme: "light" | "dark" } | null;
if (settings?.theme) {
currentTheme.value = settings.theme;
}
});
onMounted(() => {
loadSettings();
});
</script>
<template>
<div class="h-full flex justify-center items-center" :style="{ backgroundColor: currentTheme === 'dark' ? '#202227' : '#FFFFFF' }">
<div class="flex flex-col gap-4">
<div class="flex gap-2">
<!-- Light theme button -->
<Button
label="Light"
@click="updateTheme('light')"
:disabled="currentTheme === 'light'"
/>
<!-- Dark theme button -->
<Button
label="Dark"
@click="updateTheme('dark')"
:disabled="currentTheme === 'dark'"
/>
</div>
</div>
</div>
</template>
Script Breakdown
First, the <script>
block with TypeScript support is opened and the Caido SDK is imported along with three Vue reactivity utilities:
ref
: Tracks value changes to automatically update the user interface.onMounted
: Registers a callback function that will be executed after a component has been added to the user interface.watch
: Monitors for changes to reactive components and executes a callback function when a change occurs.
<script setup lang="ts">
import Button from "primevue/button";
import { useSDK } from "@/plugins/sdk";
import { ref, onMounted, watch } from "vue";
The Caido SDK is initialized so the plugin can access its functionality.
const sdk = useSDK();
A variable named currentTheme
is defined that will dynamically store the theme selection, starting with null
to account for the first initialization.
const currentTheme = ref<"light" | "dark" | null>(null);
Next, an asynchronous function named updateTheme
is defined to save the current theme selection in storage using sdk.storage.set()
.
const updateTheme = async (theme: "light" | "dark") => {
await sdk.storage.set({ theme });
currentTheme.value = theme;
};
To apply the theme to the user interface, the applyTheme
function switches the background color of the user interface.
const applyTheme = (theme: "light" | "dark") => {
const root = document.getElementById('plugin--frontend-vue');
if (root) {
if (theme === "dark") {
root.style.backgroundColor = "#202227";
} else {
root.style.backgroundColor = "#FFFFFF";
}
}
};
To retrieve the saved theme setting from storage, sdk.storage.get()
is called by the loadSettings
function. If a theme exists in storage, it will be applied. If there is no saved theme selection, the theme will default to light.
const loadSettings = () => {
const settings = sdk.storage.get() as { theme: "light" | "dark" } | null;
if (settings?.theme) {
currentTheme.value = settings.theme;
applyTheme(settings.theme);
} else {
currentTheme.value = "light";
applyTheme("light");
}
};
The watch
utility is used to monitor for changes to the value of the currentTheme
variable and calls the applyTheme
function if the value changes:
watch(currentTheme, (newTheme) => {
if (newTheme) {
applyTheme(newTheme);
}
});
With sdk.storage.onChange()
, if a theme change is made from other user interfaces, they will be applied to this plugin's user interface as well.
sdk.storage.onChange((newSettings) => {
const settings = newSettings as { theme: "light" | "dark" } | null;
if (settings?.theme) {
currentTheme.value = settings.theme;
}
});
When the page is loaded, the loadSettings()
function will be called.
onMounted(() => {
loadSettings();
});
</script>
A button for both themes are added to the user interface that will call the updateTheme
function to update the saved theme in storage when clicked. If the corresponding theme is already saved, its button will be disabled.
<template>
<div class="h-full flex justify-center items-center" :style="{ backgroundColor: currentTheme === 'dark' ? '#202227' : '#FFFFFF' }">
<div class="flex flex-col gap-4">
<div class="flex gap-2">
<!-- Light theme button -->
<Button
label="Light"
@click="updateTheme('light')"
:disabled="currentTheme === 'light'"
/>
<!-- Dark theme button -->
<Button
label="Dark"
@click="updateTheme('dark')"
:disabled="currentTheme === 'dark'"
/>
</div>
</div>
</div>
</template>
INFO
Although frontend storage actually exists in the backend, it is inaccessible by the backend component. To share data with the backend component of a plugin, you will need to create and call a custom function.
Stored data needs to be JSON serializable.