mirror of
https://github.com/nicotsx/ironmount.git
synced 2025-12-10 12:10:51 +01:00
feat(repositories): add google cloud storage support
This commit is contained in:
@@ -30,6 +30,7 @@ type Props = {
|
|||||||
const defaultValuesForType = {
|
const defaultValuesForType = {
|
||||||
local: { backend: "local" as const, compressionMode: "auto" as const },
|
local: { backend: "local" as const, compressionMode: "auto" as const },
|
||||||
s3: { backend: "s3" as const, compressionMode: "auto" as const },
|
s3: { backend: "s3" as const, compressionMode: "auto" as const },
|
||||||
|
gcs: { backend: "gcs" as const, compressionMode: "auto" as const },
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateRepositoryForm = ({
|
export const CreateRepositoryForm = ({
|
||||||
@@ -100,6 +101,7 @@ export const CreateRepositoryForm = ({
|
|||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="local">Local</SelectItem>
|
<SelectItem value="local">Local</SelectItem>
|
||||||
<SelectItem value="s3">S3</SelectItem>
|
<SelectItem value="s3">S3</SelectItem>
|
||||||
|
<SelectItem value="gcs">Google Cloud Storage</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormDescription>Choose the storage backend for this repository.</FormDescription>
|
<FormDescription>Choose the storage backend for this repository.</FormDescription>
|
||||||
@@ -195,6 +197,53 @@ export const CreateRepositoryForm = ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{watchedBackend === "gcs" && (
|
||||||
|
<>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="bucket"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Bucket</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="my-backup-bucket" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>GCS bucket name for storing backups.</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="projectId"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Project ID</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input placeholder="my-gcp-project-123" {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>Google Cloud project ID.</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="credentialsJson"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem>
|
||||||
|
<FormLabel>Service Account JSON</FormLabel>
|
||||||
|
<FormControl>
|
||||||
|
<Input type="password" placeholder="Paste service account JSON key..." {...field} />
|
||||||
|
</FormControl>
|
||||||
|
<FormDescription>Service account JSON credentials for authentication.</FormDescription>
|
||||||
|
<FormMessage />
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{mode === "update" && (
|
{mode === "update" && (
|
||||||
<Button type="submit" className="w-full" loading={loading}>
|
<Button type="submit" className="w-full" loading={loading}>
|
||||||
Save Changes
|
Save Changes
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export const RepositoryIcon = ({ backend, className = "h-4 w-4" }: Props) => {
|
|||||||
return <HardDrive className={className} />;
|
return <HardDrive className={className} />;
|
||||||
case "s3":
|
case "s3":
|
||||||
return <Cloud className={className} />;
|
return <Cloud className={className} />;
|
||||||
|
case "gcs":
|
||||||
|
return <Cloud className={className} />;
|
||||||
default:
|
default:
|
||||||
return <Database className={className} />;
|
return <Database className={className} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export default function Repositories({ loaderData }: Route.ComponentProps) {
|
|||||||
<SelectItem value="local">Local</SelectItem>
|
<SelectItem value="local">Local</SelectItem>
|
||||||
<SelectItem value="sftp">SFTP</SelectItem>
|
<SelectItem value="sftp">SFTP</SelectItem>
|
||||||
<SelectItem value="s3">S3</SelectItem>
|
<SelectItem value="s3">S3</SelectItem>
|
||||||
|
<SelectItem value="gcs">Google Cloud Storage</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
{(searchQuery || statusFilter || backendFilter) && (
|
{(searchQuery || statusFilter || backendFilter) && (
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ const encryptConfig = async (config: RepositoryConfig): Promise<RepositoryConfig
|
|||||||
encryptedConfig.accessKeyId = await cryptoUtils.encrypt(config.accessKeyId);
|
encryptedConfig.accessKeyId = await cryptoUtils.encrypt(config.accessKeyId);
|
||||||
encryptedConfig.secretAccessKey = await cryptoUtils.encrypt(config.secretAccessKey);
|
encryptedConfig.secretAccessKey = await cryptoUtils.encrypt(config.secretAccessKey);
|
||||||
break;
|
break;
|
||||||
|
case "gcs":
|
||||||
|
encryptedConfig.credentialsJson = await cryptoUtils.encrypt(config.credentialsJson);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return encryptedConfig as RepositoryConfig;
|
return encryptedConfig as RepositoryConfig;
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ const buildRepoUrl = (config: RepositoryConfig): string => {
|
|||||||
return `${REPOSITORY_BASE}/${config.name}`;
|
return `${REPOSITORY_BASE}/${config.name}`;
|
||||||
case "s3":
|
case "s3":
|
||||||
return `s3:${config.endpoint}/${config.bucket}`;
|
return `s3:${config.endpoint}/${config.bucket}`;
|
||||||
|
case "gcs":
|
||||||
|
return `gs:${config.bucket}:/`;
|
||||||
default: {
|
default: {
|
||||||
throw new Error(`Unsupported repository backend: ${JSON.stringify(config)}`);
|
throw new Error(`Unsupported repository backend: ${JSON.stringify(config)}`);
|
||||||
}
|
}
|
||||||
@@ -91,6 +93,14 @@ const buildEnv = async (config: RepositoryConfig) => {
|
|||||||
env.AWS_ACCESS_KEY_ID = await cryptoUtils.decrypt(config.accessKeyId);
|
env.AWS_ACCESS_KEY_ID = await cryptoUtils.decrypt(config.accessKeyId);
|
||||||
env.AWS_SECRET_ACCESS_KEY = await cryptoUtils.decrypt(config.secretAccessKey);
|
env.AWS_SECRET_ACCESS_KEY = await cryptoUtils.decrypt(config.secretAccessKey);
|
||||||
break;
|
break;
|
||||||
|
case "gcs": {
|
||||||
|
const decryptedCredentials = await cryptoUtils.decrypt(config.credentialsJson);
|
||||||
|
const credentialsPath = path.join("/tmp", `gcs-credentials-${crypto.randomBytes(8).toString("hex")}.json`);
|
||||||
|
await fs.writeFile(credentialsPath, decryptedCredentials, { mode: 0o600 });
|
||||||
|
env.GOOGLE_PROJECT_ID = config.projectId;
|
||||||
|
env.GOOGLE_APPLICATION_CREDENTIALS = credentialsPath;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return env;
|
return env;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { type } from "arktype";
|
|||||||
export const REPOSITORY_BACKENDS = {
|
export const REPOSITORY_BACKENDS = {
|
||||||
local: "local",
|
local: "local",
|
||||||
s3: "s3",
|
s3: "s3",
|
||||||
|
gcs: "gcs",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
|
export type RepositoryBackend = keyof typeof REPOSITORY_BACKENDS;
|
||||||
@@ -20,7 +21,14 @@ export const localRepositoryConfigSchema = type({
|
|||||||
name: "string",
|
name: "string",
|
||||||
});
|
});
|
||||||
|
|
||||||
export const repositoryConfigSchema = s3RepositoryConfigSchema.or(localRepositoryConfigSchema);
|
export const gcsRepositoryConfigSchema = type({
|
||||||
|
backend: "'gcs'",
|
||||||
|
bucket: "string",
|
||||||
|
projectId: "string",
|
||||||
|
credentialsJson: "string",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const repositoryConfigSchema = s3RepositoryConfigSchema.or(localRepositoryConfigSchema).or(gcsRepositoryConfigSchema);
|
||||||
|
|
||||||
export type RepositoryConfig = typeof repositoryConfigSchema.infer;
|
export type RepositoryConfig = typeof repositoryConfigSchema.infer;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user