FileUpload: add support for encrypting.host (#1139)
* FileUpload: add support for encrypting.host * so you can throw me to the wolves tomorrow i will come back leader of the whole pack beat me black and blueee every word will shape me every scar will build my throoone
This commit is contained in:
parent
03b6c19441
commit
7aebbe9707
3 changed files with 217 additions and 0 deletions
|
|
@ -24,6 +24,7 @@ const serviceOptions = [
|
|||
{ label: "Zipline", value: ServiceType.ZIPLINE, default: true },
|
||||
{ label: "E-Z Host", value: ServiceType.EZHOST },
|
||||
{ label: "Nest", value: ServiceType.NEST },
|
||||
{ label: "Encrypting.host", value: ServiceType.ENCRYPTINGHOST },
|
||||
{ label: "S3-Compatible", value: ServiceType.S3 },
|
||||
{ label: "Catbox.moe", value: ServiceType.CATBOX },
|
||||
...(IS_DISCORD_DESKTOP ? [{ label: "0x0.st", value: ServiceType.ZEROX0 }] : []),
|
||||
|
|
@ -50,6 +51,13 @@ const embedProxyOptions = [
|
|||
{ label: "discord.nfp.is", value: "nfp" }
|
||||
];
|
||||
|
||||
const encryptingHostUrlStyleOptions = [
|
||||
{ label: "Query", value: "query", default: true },
|
||||
{ label: "Param", value: "param" },
|
||||
{ label: "Fake Link", value: "fakelink" },
|
||||
{ label: "Embed", value: "embed" }
|
||||
];
|
||||
|
||||
export const settings = definePluginSettings({
|
||||
serviceType: {
|
||||
type: OptionType.SELECT,
|
||||
|
|
@ -87,6 +95,43 @@ export const settings = definePluginSettings({
|
|||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostKey: {
|
||||
type: OptionType.STRING,
|
||||
description: "Encrypting.host API key",
|
||||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostUrlStyle: {
|
||||
type: OptionType.SELECT,
|
||||
description: "Encrypting.host URL style",
|
||||
options: encryptingHostUrlStyleOptions,
|
||||
default: "query",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostDomains: {
|
||||
type: OptionType.STRING,
|
||||
description: "Encrypting.host domains JSON list",
|
||||
default: "[\"offensive\"]",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostTitle: {
|
||||
type: OptionType.STRING,
|
||||
description: "Optional Encrypting.host embed title",
|
||||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostColor: {
|
||||
type: OptionType.STRING,
|
||||
description: "Optional Encrypting.host embed color",
|
||||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
encryptingHostFakelink: {
|
||||
type: OptionType.STRING,
|
||||
description: "Optional Encrypting.host fake link",
|
||||
default: "",
|
||||
hidden: true
|
||||
},
|
||||
s3Endpoint: {
|
||||
type: OptionType.STRING,
|
||||
description: "S3-compatible endpoint URL",
|
||||
|
|
@ -421,6 +466,7 @@ export function SettingsComponent() {
|
|||
const sharexFileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const isNest = store.serviceType === ServiceType.NEST;
|
||||
const isEzHost = store.serviceType === ServiceType.EZHOST;
|
||||
const isEncryptingHost = store.serviceType === ServiceType.ENCRYPTINGHOST;
|
||||
const isS3 = store.serviceType === ServiceType.S3;
|
||||
const isZipline = store.serviceType === ServiceType.ZIPLINE;
|
||||
const isCatbox = store.serviceType === ServiceType.CATBOX;
|
||||
|
|
@ -527,6 +573,59 @@ export function SettingsComponent() {
|
|||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isEncryptingHost && (
|
||||
<SettingGroup name="Encrypting.host" description="Connection details for Encrypting.host uploads.">
|
||||
<SettingTextInput
|
||||
name="Encrypting.host API Key"
|
||||
description="Your Encrypting.host API key"
|
||||
value={(store as { encryptingHostKey?: string; }).encryptingHostKey || ""}
|
||||
onChange={v => (store as { encryptingHostKey?: string; }).encryptingHostKey = v}
|
||||
placeholder="Your Encrypting.host API key"
|
||||
/>
|
||||
<SettingsSection name="URL Style" description="How Encrypting.host should format returned links.">
|
||||
<Select
|
||||
options={encryptingHostUrlStyleOptions}
|
||||
isSelected={v => v === (store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle}
|
||||
select={v => {
|
||||
(store as { encryptingHostUrlStyle?: string; }).encryptingHostUrlStyle = v;
|
||||
update();
|
||||
}}
|
||||
serialize={v => v}
|
||||
placeholder="Select URL style"
|
||||
/>
|
||||
</SettingsSection>
|
||||
<SettingsSection name="Domains JSON" description={"JSON array of domains to use, for example [\"offensive\"]."}>
|
||||
<TextArea
|
||||
value={(store as { encryptingHostDomains?: string; }).encryptingHostDomains || ""}
|
||||
rows={3}
|
||||
placeholder='["offensive"]'
|
||||
onChange={v => (store as { encryptingHostDomains?: string; }).encryptingHostDomains = v}
|
||||
/>
|
||||
</SettingsSection>
|
||||
<SettingTextInput
|
||||
name="Embed Title"
|
||||
description="Optional title for embed style responses."
|
||||
value={(store as { encryptingHostTitle?: string; }).encryptingHostTitle || ""}
|
||||
onChange={v => (store as { encryptingHostTitle?: string; }).encryptingHostTitle = v}
|
||||
placeholder="Optional title"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Embed Color"
|
||||
description="Optional color for embed style responses."
|
||||
value={(store as { encryptingHostColor?: string; }).encryptingHostColor || ""}
|
||||
onChange={v => (store as { encryptingHostColor?: string; }).encryptingHostColor = v}
|
||||
placeholder="Optional color"
|
||||
/>
|
||||
<SettingTextInput
|
||||
name="Fake Link"
|
||||
description="Optional fake link value for fakelink style responses."
|
||||
value={(store as { encryptingHostFakelink?: string; }).encryptingHostFakelink || ""}
|
||||
onChange={v => (store as { encryptingHostFakelink?: string; }).encryptingHostFakelink = v}
|
||||
placeholder="Optional fake link"
|
||||
/>
|
||||
</SettingGroup>
|
||||
)}
|
||||
|
||||
{isS3 && (
|
||||
<SettingGroup name="S3-Compatible Storage" description="Connection details and object naming for your bucket.">
|
||||
<SettingTextInput
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ export enum ServiceType {
|
|||
ZIPLINE = "zipline",
|
||||
NEST = "nest",
|
||||
EZHOST = "ezhost",
|
||||
ENCRYPTINGHOST = "encryptinghost",
|
||||
S3 = "s3",
|
||||
CATBOX = "catbox",
|
||||
ZEROX0 = "0x0",
|
||||
|
|
@ -26,6 +27,7 @@ export const serviceLabels: Record<ServiceType, string> = {
|
|||
[ServiceType.ZIPLINE]: "Zipline",
|
||||
[ServiceType.NEST]: "Nest",
|
||||
[ServiceType.EZHOST]: "E-Z Host",
|
||||
[ServiceType.ENCRYPTINGHOST]: "Encrypting.host",
|
||||
[ServiceType.S3]: "S3-Compatible",
|
||||
[ServiceType.CATBOX]: "Catbox",
|
||||
[ServiceType.ZEROX0]: "0x0.st",
|
||||
|
|
@ -44,6 +46,7 @@ export const fallbackServiceOrder: ServiceType[] = [
|
|||
ServiceType.ZIPLINE,
|
||||
ServiceType.EZHOST,
|
||||
ServiceType.NEST,
|
||||
ServiceType.ENCRYPTINGHOST,
|
||||
ServiceType.S3,
|
||||
ServiceType.CATBOX,
|
||||
ServiceType.ZEROX0,
|
||||
|
|
|
|||
|
|
@ -448,6 +448,117 @@ async function uploadToNest(fileBlob: Blob, filename: string): Promise<string> {
|
|||
throw new Error("No URL returned from upload");
|
||||
}
|
||||
|
||||
type EncryptingHostUrlStyle = "query" | "param" | "fakelink" | "embed";
|
||||
|
||||
function parseEncryptingHostDomains(raw: string): string[] {
|
||||
let parsed: unknown;
|
||||
|
||||
try {
|
||||
parsed = JSON.parse(raw);
|
||||
} catch {
|
||||
throw new Error("Encrypting.host domains must be a JSON array of non-empty strings");
|
||||
}
|
||||
|
||||
if (!Array.isArray(parsed) || parsed.some(domain => typeof domain !== "string" || !domain.trim())) {
|
||||
throw new Error("Encrypting.host domains must be a JSON array of non-empty strings");
|
||||
}
|
||||
|
||||
return parsed.map(domain => domain.trim());
|
||||
}
|
||||
|
||||
function getEncryptingHostConfig(): {
|
||||
key: string;
|
||||
urlStyle: EncryptingHostUrlStyle;
|
||||
domains: string[];
|
||||
title: string;
|
||||
color: string;
|
||||
fakelink: string;
|
||||
} {
|
||||
const {
|
||||
encryptingHostKey,
|
||||
encryptingHostUrlStyle,
|
||||
encryptingHostDomains,
|
||||
encryptingHostTitle,
|
||||
encryptingHostColor,
|
||||
encryptingHostFakelink
|
||||
} = settings.store as {
|
||||
encryptingHostKey?: string;
|
||||
encryptingHostUrlStyle?: string;
|
||||
encryptingHostDomains?: string;
|
||||
encryptingHostTitle?: string;
|
||||
encryptingHostColor?: string;
|
||||
encryptingHostFakelink?: string;
|
||||
};
|
||||
|
||||
const key = encryptingHostKey?.trim() || "";
|
||||
if (!key) {
|
||||
throw new Error("Encrypting.host API key is required");
|
||||
}
|
||||
|
||||
const style = encryptingHostUrlStyle || "query";
|
||||
if (style !== "query" && style !== "param" && style !== "fakelink" && style !== "embed") {
|
||||
throw new Error("Invalid Encrypting.host URL style");
|
||||
}
|
||||
|
||||
const domainsRaw = encryptingHostDomains?.trim() || "[\"offensive\"]";
|
||||
|
||||
return {
|
||||
key,
|
||||
urlStyle: style,
|
||||
domains: parseEncryptingHostDomains(domainsRaw),
|
||||
title: encryptingHostTitle?.trim() || "",
|
||||
color: encryptingHostColor?.trim() || "",
|
||||
fakelink: encryptingHostFakelink?.trim() || ""
|
||||
};
|
||||
}
|
||||
|
||||
async function uploadToEncryptingHost(fileBlob: Blob, filename: string): Promise<string> {
|
||||
const config = getEncryptingHostConfig();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("password", makeRandomHex(16));
|
||||
formData.append("userKey", config.key);
|
||||
formData.append("urlStyle", config.urlStyle);
|
||||
formData.append("domains", JSON.stringify(config.domains));
|
||||
if (config.title) {
|
||||
formData.append("title", config.title);
|
||||
}
|
||||
if (config.color) {
|
||||
formData.append("color", config.color);
|
||||
}
|
||||
if (config.fakelink) {
|
||||
formData.append("fakelink", config.fakelink);
|
||||
}
|
||||
formData.append("file", fileBlob, filename);
|
||||
|
||||
const response = await uploadRequestWithTimeout("https://encrypting.host/upload", {
|
||||
method: "POST",
|
||||
body: formData
|
||||
});
|
||||
|
||||
const text = await response.text();
|
||||
let data: { url?: string; error?: string; } | null = null;
|
||||
|
||||
try {
|
||||
data = text ? JSON.parse(text) as { url?: string; error?: string; } : null;
|
||||
} catch {
|
||||
data = null;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Upload failed: ${response.status} ${text}`);
|
||||
}
|
||||
|
||||
const parsedUrl = data?.url?.trim() || "";
|
||||
const fallbackUrl = text.trim().startsWith("http") ? text.trim() : "";
|
||||
const url = parsedUrl || fallbackUrl;
|
||||
if (!url) {
|
||||
throw new Error("No URL returned from upload");
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function isConfigured(): boolean {
|
||||
const {
|
||||
serviceType,
|
||||
|
|
@ -465,6 +576,8 @@ export function isConfigured(): boolean {
|
|||
return Boolean(nestToken);
|
||||
case ServiceType.EZHOST:
|
||||
return Boolean((settings.store as { ezHostKey?: string; }).ezHostKey);
|
||||
case ServiceType.ENCRYPTINGHOST:
|
||||
return Boolean((settings.store as { encryptingHostKey?: string; }).encryptingHostKey);
|
||||
case ServiceType.S3:
|
||||
return isS3Configured();
|
||||
case ServiceType.CATBOX:
|
||||
|
|
@ -867,6 +980,8 @@ async function uploadToService(serviceType: ServiceType, fileBlob: Blob, filenam
|
|||
return uploadToNest(fileBlob, filename);
|
||||
case ServiceType.EZHOST:
|
||||
return uploadToEzHost(fileBlob, filename);
|
||||
case ServiceType.ENCRYPTINGHOST:
|
||||
return uploadToEncryptingHost(fileBlob, filename);
|
||||
case ServiceType.S3:
|
||||
return uploadToS3(fileBlob, filename, Native, uploadRequestWithTimeout);
|
||||
case ServiceType.CATBOX:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue