Refactor type usage and improve data normalization
Updated several components to use explicit TypeScript types for better type safety. Normalized appriseUrls to always be an array in auto-sync settings API. Improved handling of optional server_id in BackupsTab and adjusted IP detection logic in InstalledScriptsTab. Removed unnecessary eslint-disable comments and improved code clarity in various places.
This commit is contained in:
@@ -1,15 +1,23 @@
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import eslintPluginNext from "@next/eslint-plugin-next";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: import.meta.dirname,
|
||||
});
|
||||
import reactPlugin from "eslint-plugin-react";
|
||||
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: [".next", "next-env.d.ts", "postcss.config.js", "prettier.config.js"],
|
||||
},
|
||||
...compat.extends("next/core-web-vitals"),
|
||||
{
|
||||
plugins: {
|
||||
"@next/next": eslintPluginNext,
|
||||
"react": reactPlugin,
|
||||
"react-hooks": reactHooksPlugin,
|
||||
},
|
||||
rules: {
|
||||
...eslintPluginNext.configs.recommended.rules,
|
||||
...eslintPluginNext.configs["core-web-vitals"].rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["**/*.ts", "**/*.tsx"],
|
||||
extends: [
|
||||
|
||||
@@ -97,7 +97,7 @@ export function AuthProvider({ children }: AuthProviderProps) {
|
||||
|
||||
const checkAuth = useCallback(() => {
|
||||
return checkAuthInternal(0);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
||||
}, []);
|
||||
|
||||
const login = async (
|
||||
|
||||
@@ -32,7 +32,7 @@ interface Backup {
|
||||
storage_name: string;
|
||||
storage_type: string;
|
||||
discovered_at: Date;
|
||||
server_id: number;
|
||||
server_id?: number;
|
||||
server_name: string | null;
|
||||
server_color: string | null;
|
||||
}
|
||||
@@ -163,7 +163,6 @@ export function BackupsTab() {
|
||||
}
|
||||
setHasAutoDiscovered(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [hasAutoDiscovered, isLoading, backupsData]);
|
||||
|
||||
const handleDiscoverBackups = () => {
|
||||
@@ -188,7 +187,7 @@ export function BackupsTab() {
|
||||
restoreMutation.mutate({
|
||||
backupId: selectedBackup.backup.id,
|
||||
containerId: selectedBackup.containerId,
|
||||
serverId: selectedBackup.backup.server_id,
|
||||
serverId: selectedBackup.backup.server_id ?? 0,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ export function FilterBar({
|
||||
|
||||
{/* Repository Filter Buttons - Only show if more than one enabled repo */}
|
||||
{enabledRepos.length > 1 &&
|
||||
enabledRepos.map((repo) => {
|
||||
enabledRepos.map((repo: { id: number; url: string }) => {
|
||||
const repoUrl = String(repo.url);
|
||||
const isSelected =
|
||||
filters.selectedRepositories.includes(repoUrl);
|
||||
|
||||
@@ -1704,7 +1704,15 @@ export function GeneralSettingsModal({
|
||||
{repositoriesData?.success &&
|
||||
repositoriesData.repositories.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{repositoriesData.repositories.map((repo) => (
|
||||
{repositoriesData.repositories.map(
|
||||
(repo: {
|
||||
id: number;
|
||||
url: string;
|
||||
enabled: boolean;
|
||||
is_default: boolean;
|
||||
is_removable: boolean;
|
||||
priority: number;
|
||||
}) => (
|
||||
<div
|
||||
key={repo.id}
|
||||
className="border-border flex items-center justify-between gap-3 rounded-lg border p-3"
|
||||
@@ -1831,7 +1839,8 @@ export function GeneralSettingsModal({
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
|
||||
@@ -331,7 +331,7 @@ export function InstalledScriptsTab() {
|
||||
setAutoDetectStatus({
|
||||
type: "success",
|
||||
message: data.success
|
||||
? `Detected IP: ${data.ip}`
|
||||
? `Detected IP: ${data.detectedIp ?? "unknown"}`
|
||||
: (data.error ?? "Failed to detect Web UI"),
|
||||
});
|
||||
setTimeout(
|
||||
@@ -359,15 +359,10 @@ export function InstalledScriptsTab() {
|
||||
{ enabled: false }, // Only fetch when explicitly called
|
||||
);
|
||||
|
||||
const fetchStorages = async (serverId: number, forceRefresh = false) => {
|
||||
const fetchStorages = async (serverId: number, _forceRefresh = false) => {
|
||||
setIsLoadingStorages(true);
|
||||
try {
|
||||
const result = await getBackupStoragesQuery.refetch({
|
||||
queryKey: [
|
||||
"installedScripts.getBackupStorages",
|
||||
{ serverId, forceRefresh },
|
||||
],
|
||||
});
|
||||
const result = await getBackupStoragesQuery.refetch();
|
||||
if (result.data?.success) {
|
||||
setBackupStorages(result.data.storages);
|
||||
} else {
|
||||
|
||||
@@ -74,7 +74,7 @@ export function ServerForm({
|
||||
// Check for IPv6 with zone identifier (link-local addresses like fe80::...%eth0)
|
||||
let ipv6Address = trimmed;
|
||||
const zoneIdMatch = /^(.+)%([a-zA-Z0-9_\-]+)$/.exec(trimmed);
|
||||
if (zoneIdMatch) {
|
||||
if (zoneIdMatch?.[1] && zoneIdMatch[2]) {
|
||||
ipv6Address = zoneIdMatch[1];
|
||||
// Zone identifier should be a valid interface name (alphanumeric, underscore, hyphen)
|
||||
const zoneId = zoneIdMatch[2];
|
||||
|
||||
@@ -380,7 +380,7 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
|
||||
wsRef.current.close();
|
||||
}
|
||||
};
|
||||
}, [scriptPath, mode, server, isUpdate, isShell, containerId, isMobile]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [scriptPath, mode, server, isUpdate, isShell, containerId, isMobile]);
|
||||
|
||||
const startScript = () => {
|
||||
if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN && !isRunning) {
|
||||
|
||||
@@ -187,7 +187,16 @@ export async function POST(request: NextRequest) {
|
||||
}
|
||||
|
||||
// Update the global service instance with new settings
|
||||
autoSyncService.saveSettings(settings);
|
||||
// Normalize appriseUrls to always be an array
|
||||
const normalizedSettings = {
|
||||
...settings,
|
||||
appriseUrls: Array.isArray(settings.appriseUrls)
|
||||
? settings.appriseUrls
|
||||
: settings.appriseUrls
|
||||
? [settings.appriseUrls]
|
||||
: undefined
|
||||
};
|
||||
autoSyncService.saveSettings(normalizedSettings);
|
||||
|
||||
if (settings.autoSyncEnabled) {
|
||||
autoSyncService.scheduleAutoSync();
|
||||
|
||||
@@ -476,7 +476,8 @@ export const installedScriptsRouter = createTRPCRouter({
|
||||
const scripts = await db.getAllInstalledScripts();
|
||||
|
||||
// Transform scripts to flatten server data for frontend compatibility
|
||||
const transformedScripts = await Promise.all(scripts.map(async (script) => {
|
||||
|
||||
const transformedScripts = await Promise.all(scripts.map(async (script: any) => {
|
||||
// Determine if it's a VM or LXC
|
||||
let is_vm = false;
|
||||
if (script.container_id && script.server_id) {
|
||||
@@ -522,7 +523,8 @@ export const installedScriptsRouter = createTRPCRouter({
|
||||
const scripts = await db.getInstalledScriptsByServer(input.serverId);
|
||||
|
||||
// Transform scripts to flatten server data for frontend compatibility
|
||||
const transformedScripts = await Promise.all(scripts.map(async (script) => {
|
||||
|
||||
const transformedScripts = await Promise.all(scripts.map(async (script: any) => {
|
||||
// Determine if it's a VM or LXC
|
||||
let is_vm = false;
|
||||
if (script.container_id && script.server_id) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
import { z } from "zod";
|
||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
|
||||
import { scriptManager } from "~/server/lib/scripts";
|
||||
|
||||
Reference in New Issue
Block a user