mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-07-04 08:20:13 +02:00
feat(share, config): more variables, placeholder and reset default (#132)
* More email share vars + unfinished placeolders config {desc} {expires} vars (unfinished) config placeholder vals * done * migrate * edit seed * removed comments * refactor: replace dependecy `luxon` with `moment` * update shareRecipientsMessage message * chore: remove `luxon` * fix: grammatically incorrect `shareRecipientsMessage` message * changed to defaultValue and value instead * fix: don't expose defaultValue to non admin user * fix: update default value if default value changes * refactor: set config value to null instead of a empty string * refactor: merge two migrations into one * fix value check empty --------- Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
parent
a0d1d98e24
commit
beece56327
|
@ -0,0 +1,23 @@
|
||||||
|
-- RedefineTables
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Config" (
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"category" TEXT NOT NULL,
|
||||||
|
"type" TEXT NOT NULL,
|
||||||
|
"value" TEXT,
|
||||||
|
"defaultValue" TEXT NOT NULL DEFAULT '',
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
"obscured" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"secret" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"locked" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"order" INTEGER NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY ("name", "category")
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Config" ("category", "description", "locked", "name", "obscured", "order", "secret", "type", "updatedAt", "value") SELECT "category", "description", "locked", "name", "obscured", "order", "secret", "type", "updatedAt", "value" FROM "Config";
|
||||||
|
DROP TABLE "Config";
|
||||||
|
ALTER TABLE "new_Config" RENAME TO "Config";
|
||||||
|
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON;
|
|
@ -134,7 +134,8 @@ model Config {
|
||||||
name String
|
name String
|
||||||
category String
|
category String
|
||||||
type String
|
type String
|
||||||
value String
|
defaultValue String @default("")
|
||||||
|
value String?
|
||||||
description String
|
description String
|
||||||
obscured Boolean @default(false)
|
obscured Boolean @default(false)
|
||||||
secret Boolean @default(true)
|
secret Boolean @default(true)
|
||||||
|
|
|
@ -6,7 +6,7 @@ const configVariables: ConfigVariables = {
|
||||||
jwtSecret: {
|
jwtSecret: {
|
||||||
description: "Long random string used to sign JWT tokens",
|
description: "Long random string used to sign JWT tokens",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: crypto.randomBytes(256).toString("base64"),
|
defaultValue: crypto.randomBytes(256).toString("base64"),
|
||||||
locked: true,
|
locked: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -14,20 +14,20 @@ const configVariables: ConfigVariables = {
|
||||||
appName: {
|
appName: {
|
||||||
description: "Name of the application",
|
description: "Name of the application",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "Pingvin Share",
|
defaultValue: "Pingvin Share",
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
appUrl: {
|
appUrl: {
|
||||||
description: "On which URL Pingvin Share is available",
|
description: "On which URL Pingvin Share is available",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "http://localhost:3000",
|
defaultValue: "http://localhost:3000",
|
||||||
|
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
showHomePage: {
|
showHomePage: {
|
||||||
description: "Whether to show the home page",
|
description: "Whether to show the home page",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: "true",
|
defaultValue: "true",
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -35,21 +35,21 @@ const configVariables: ConfigVariables = {
|
||||||
allowRegistration: {
|
allowRegistration: {
|
||||||
description: "Whether registration is allowed",
|
description: "Whether registration is allowed",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: "true",
|
defaultValue: "true",
|
||||||
|
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
allowUnauthenticatedShares: {
|
allowUnauthenticatedShares: {
|
||||||
description: "Whether unauthorized users can create shares",
|
description: "Whether unauthorized users can create shares",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: "false",
|
defaultValue: "false",
|
||||||
|
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
maxSize: {
|
maxSize: {
|
||||||
description: "Maximum share size in bytes",
|
description: "Maximum share size in bytes",
|
||||||
type: "number",
|
type: "number",
|
||||||
value: "1073741824",
|
defaultValue: "1073741824",
|
||||||
|
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
|
@ -59,7 +59,7 @@ const configVariables: ConfigVariables = {
|
||||||
description:
|
description:
|
||||||
"Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
|
"Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: "false",
|
defaultValue: "false",
|
||||||
|
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
|
@ -67,53 +67,53 @@ const configVariables: ConfigVariables = {
|
||||||
description:
|
description:
|
||||||
"Subject of the email which gets sent to the share recipients.",
|
"Subject of the email which gets sent to the share recipients.",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "Files shared with you",
|
defaultValue: "Files shared with you",
|
||||||
},
|
},
|
||||||
shareRecipientsMessage: {
|
shareRecipientsMessage: {
|
||||||
description:
|
description:
|
||||||
"Message which gets sent to the share recipients. {creator} and {shareUrl} will be replaced with the creator's name and the share URL.",
|
"Message which gets sent to the share recipients.\n\nAvailable variables:\n{creator} - The username of the creator of the share\n{shareUrl} - The URL of the share\n{desc} - The description of the share\n{expires} - The expiration date of the share\n\nVariables will be replaced with the actual values.",
|
||||||
type: "text",
|
type: "text",
|
||||||
value:
|
defaultValue:
|
||||||
"Hey!\n{creator} shared some files with you. View or download the files with this link: {shareUrl}\nShared securely with Pingvin Share 🐧",
|
"Hey!\n\n{creator} shared some files with you, view or download the files with this link: {shareUrl}\n\nThe share will expire {expires}.\n\nNote: {desc}\n\nShared securely with Pingvin Share 🐧",
|
||||||
},
|
},
|
||||||
reverseShareSubject: {
|
reverseShareSubject: {
|
||||||
description:
|
description:
|
||||||
"Subject of the email which gets sent when someone created a share with your reverse share link.",
|
"Subject of the email which gets sent when someone created a share with your reverse share link.",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "Reverse share link used",
|
defaultValue: "Reverse share link used",
|
||||||
},
|
},
|
||||||
reverseShareMessage: {
|
reverseShareMessage: {
|
||||||
description:
|
description:
|
||||||
"Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
|
"Message which gets sent when someone created a share with your reverse share link. {shareUrl} will be replaced with the creator's name and the share URL.",
|
||||||
type: "text",
|
type: "text",
|
||||||
value:
|
defaultValue:
|
||||||
"Hey!\nA share was just created with your reverse share link: {shareUrl}\nShared securely with Pingvin Share 🐧",
|
"Hey!\n\nA share was just created with your reverse share link: {shareUrl}\n\nShared securely with Pingvin Share 🐧",
|
||||||
},
|
},
|
||||||
resetPasswordSubject: {
|
resetPasswordSubject: {
|
||||||
description:
|
description:
|
||||||
"Subject of the email which gets sent when a user requests a password reset.",
|
"Subject of the email which gets sent when a user requests a password reset.",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "Pingvin Share password reset",
|
defaultValue: "Pingvin Share password reset",
|
||||||
},
|
},
|
||||||
resetPasswordMessage: {
|
resetPasswordMessage: {
|
||||||
description:
|
description:
|
||||||
"Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
|
"Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
|
||||||
type: "text",
|
type: "text",
|
||||||
value:
|
defaultValue:
|
||||||
"Hey!\nYou requested a password reset. Click this link to reset your password: {url}\nThe link expires in a hour.\nPingvin Share 🐧",
|
"Hey!\n\nYou requested a password reset. Click this link to reset your password: {url}\nThe link expires in a hour.\n\nPingvin Share 🐧",
|
||||||
},
|
},
|
||||||
inviteSubject: {
|
inviteSubject: {
|
||||||
description:
|
description:
|
||||||
"Subject of the email which gets sent when an admin invites an user.",
|
"Subject of the email which gets sent when an admin invites an user.",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "Pingvin Share invite",
|
defaultValue: "Pingvin Share invite",
|
||||||
},
|
},
|
||||||
inviteMessage: {
|
inviteMessage: {
|
||||||
description:
|
description:
|
||||||
"Message which gets sent when an admin invites an user. {url} will be replaced with the invite URL and {password} with the password.",
|
"Message which gets sent when an admin invites an user. {url} will be replaced with the invite URL and {password} with the password.",
|
||||||
type: "text",
|
type: "text",
|
||||||
value:
|
defaultValue:
|
||||||
"Hey!\nYou were invited to Pingvin Share. Click this link to accept the invite: {url}\nYour password is: {password}\nPingvin Share 🐧",
|
"Hey!\n\nYou were invited to Pingvin Share. Click this link to accept the invite: {url}\n\nYour password is: {password}\n\nPingvin Share 🐧",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
smtp: {
|
smtp: {
|
||||||
|
@ -121,33 +121,33 @@ const configVariables: ConfigVariables = {
|
||||||
description:
|
description:
|
||||||
"Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
|
"Whether SMTP is enabled. Only set this to true if you entered the host, port, email, user and password of your SMTP server.",
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
value: "false",
|
defaultValue: "false",
|
||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
description: "Host of the SMTP server",
|
description: "Host of the SMTP server",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "",
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
port: {
|
port: {
|
||||||
description: "Port of the SMTP server",
|
description: "Port of the SMTP server",
|
||||||
type: "number",
|
type: "number",
|
||||||
value: "0",
|
defaultValue: "0",
|
||||||
},
|
},
|
||||||
email: {
|
email: {
|
||||||
description: "Email address which the emails get sent from",
|
description: "Email address which the emails get sent from",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "",
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
description: "Username of the SMTP server",
|
description: "Username of the SMTP server",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "",
|
defaultValue: "",
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
description: "Password of the SMTP server",
|
description: "Password of the SMTP server",
|
||||||
type: "string",
|
type: "string",
|
||||||
value: "",
|
defaultValue: "",
|
||||||
obscured: true,
|
obscured: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -21,10 +21,12 @@ export class ConfigService {
|
||||||
|
|
||||||
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
||||||
|
|
||||||
if (configVariable.type == "number") return parseInt(configVariable.value);
|
const value = configVariable.value ?? configVariable.defaultValue;
|
||||||
if (configVariable.type == "boolean") return configVariable.value == "true";
|
|
||||||
|
if (configVariable.type == "number") return parseInt(value);
|
||||||
|
if (configVariable.type == "boolean") return value == "true";
|
||||||
if (configVariable.type == "string" || configVariable.type == "text")
|
if (configVariable.type == "string" || configVariable.type == "text")
|
||||||
return configVariable.value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByCategory(category: string) {
|
async getByCategory(category: string) {
|
||||||
|
@ -35,8 +37,9 @@ export class ConfigService {
|
||||||
|
|
||||||
return configVariables.map((variable) => {
|
return configVariables.map((variable) => {
|
||||||
return {
|
return {
|
||||||
key: `${variable.category}.${variable.name}`,
|
|
||||||
...variable,
|
...variable,
|
||||||
|
key: `${variable.category}.${variable.name}`,
|
||||||
|
value: variable.value ?? variable.defaultValue,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -48,8 +51,9 @@ export class ConfigService {
|
||||||
|
|
||||||
return configVariables.map((variable) => {
|
return configVariables.map((variable) => {
|
||||||
return {
|
return {
|
||||||
key: `${variable.category}.${variable.name}`,
|
|
||||||
...variable,
|
...variable,
|
||||||
|
key: `${variable.category}.${variable.name}`,
|
||||||
|
value: variable.value ?? variable.defaultValue,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -77,7 +81,9 @@ export class ConfigService {
|
||||||
if (!configVariable || configVariable.locked)
|
if (!configVariable || configVariable.locked)
|
||||||
throw new NotFoundException("Config variable not found");
|
throw new NotFoundException("Config variable not found");
|
||||||
|
|
||||||
if (
|
if (value == "") {
|
||||||
|
value = null;
|
||||||
|
} else if (
|
||||||
typeof value != configVariable.type &&
|
typeof value != configVariable.type &&
|
||||||
typeof value == "string" &&
|
typeof value == "string" &&
|
||||||
configVariable.type != "text"
|
configVariable.type != "text"
|
||||||
|
@ -94,7 +100,7 @@ export class ConfigService {
|
||||||
name: key.split(".")[1],
|
name: key.split(".")[1],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data: { value: value.toString() },
|
data: { value: value ? value.toString() : null },
|
||||||
});
|
});
|
||||||
|
|
||||||
this.configVariables = await this.prisma.config.findMany();
|
this.configVariables = await this.prisma.config.findMany();
|
||||||
|
|
|
@ -8,6 +8,9 @@ export class AdminConfigDTO extends ConfigDTO {
|
||||||
@Expose()
|
@Expose()
|
||||||
secret: boolean;
|
secret: boolean;
|
||||||
|
|
||||||
|
@Expose()
|
||||||
|
defaultValue: string;
|
||||||
|
|
||||||
@Expose()
|
@Expose()
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import { IsNotEmpty, IsString, ValidateIf } from "class-validator";
|
import { IsNotEmpty, IsString } from "class-validator";
|
||||||
|
|
||||||
class UpdateConfigDTO {
|
class UpdateConfigDTO {
|
||||||
@IsString()
|
@IsString()
|
||||||
key: string;
|
key: string;
|
||||||
|
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@ValidateIf((dto) => dto.value !== "")
|
|
||||||
value: string | number | boolean;
|
value: string | number | boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
Logger,
|
Logger,
|
||||||
} from "@nestjs/common";
|
} from "@nestjs/common";
|
||||||
import { User } from "@prisma/client";
|
import { User } from "@prisma/client";
|
||||||
|
import * as moment from "moment";
|
||||||
import * as nodemailer from "nodemailer";
|
import * as nodemailer from "nodemailer";
|
||||||
import { ConfigService } from "src/config/config.service";
|
import { ConfigService } from "src/config/config.service";
|
||||||
|
|
||||||
|
@ -43,10 +44,12 @@ export class EmailService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendMailToShareRecepients(
|
async sendMailToShareRecipients(
|
||||||
recipientEmail: string,
|
recipientEmail: string,
|
||||||
shareId: string,
|
shareId: string,
|
||||||
creator?: User
|
creator?: User,
|
||||||
|
description?: string,
|
||||||
|
expiration?: Date
|
||||||
) {
|
) {
|
||||||
if (!this.config.get("email.enableShareEmailRecipients"))
|
if (!this.config.get("email.enableShareEmailRecipients"))
|
||||||
throw new InternalServerErrorException("Email service disabled");
|
throw new InternalServerErrorException("Email service disabled");
|
||||||
|
@ -61,6 +64,13 @@ export class EmailService {
|
||||||
.replaceAll("\\n", "\n")
|
.replaceAll("\\n", "\n")
|
||||||
.replaceAll("{creator}", creator?.username ?? "Someone")
|
.replaceAll("{creator}", creator?.username ?? "Someone")
|
||||||
.replaceAll("{shareUrl}", shareUrl)
|
.replaceAll("{shareUrl}", shareUrl)
|
||||||
|
.replaceAll("{desc}", description ?? "No description")
|
||||||
|
.replaceAll(
|
||||||
|
"{expires}",
|
||||||
|
moment(expiration).unix() != 0
|
||||||
|
? moment(expiration).fromNow()
|
||||||
|
: "in: never"
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -142,12 +142,14 @@ export class ShareService {
|
||||||
this.prisma.share.update({ where: { id }, data: { isZipReady: true } })
|
this.prisma.share.update({ where: { id }, data: { isZipReady: true } })
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send email for each recepient
|
// Send email for each recipient
|
||||||
for (const recepient of share.recipients) {
|
for (const recipient of share.recipients) {
|
||||||
await this.emailService.sendMailToShareRecepients(
|
await this.emailService.sendMailToShareRecipients(
|
||||||
recepient.email,
|
recipient.email,
|
||||||
share.id,
|
share.id,
|
||||||
share.creator
|
share.creator,
|
||||||
|
share.description,
|
||||||
|
share.expiration
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +165,7 @@ export class ShareService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if any file is malicious with ClamAV
|
// Check if any file is malicious with ClamAV
|
||||||
this.clamScanService.checkAndRemove(share.id);
|
void this.clamScanService.checkAndRemove(share.id);
|
||||||
|
|
||||||
if (share.reverseShare) {
|
if (share.reverseShare) {
|
||||||
await this.prisma.reverseShare.update({
|
await this.prisma.reverseShare.update({
|
||||||
|
@ -172,7 +174,7 @@ export class ShareService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.prisma.share.update({
|
return this.prisma.share.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: { uploadLocked: true },
|
data: { uploadLocked: true },
|
||||||
});
|
});
|
||||||
|
@ -195,14 +197,12 @@ export class ShareService {
|
||||||
include: { recipients: true },
|
include: { recipients: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
const sharesWithEmailRecipients = shares.map((share) => {
|
return shares.map((share) => {
|
||||||
return {
|
return {
|
||||||
...share,
|
...share,
|
||||||
recipients: share.recipients.map((recipients) => recipients.email),
|
recipients: share.recipients.map((recipients) => recipients.email),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
return sharesWithEmailRecipients;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id: string): Promise<any> {
|
async get(id: string): Promise<any> {
|
||||||
|
@ -222,7 +222,7 @@ export class ShareService {
|
||||||
throw new NotFoundException("Share not found");
|
throw new NotFoundException("Share not found");
|
||||||
return {
|
return {
|
||||||
...share,
|
...share,
|
||||||
hasPassword: share.security?.password ? true : false,
|
hasPassword: !!share.security?.password,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,13 @@ const AdminConfigInput = ({
|
||||||
}) => {
|
}) => {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
stringValue: configVariable.value,
|
stringValue: configVariable.value ?? configVariable.defaultValue,
|
||||||
textValue: configVariable.value,
|
textValue: configVariable.value ?? configVariable.defaultValue,
|
||||||
numberValue: parseInt(configVariable.value),
|
numberValue: parseInt(
|
||||||
booleanValue: configVariable.value == "true",
|
configVariable.value ?? configVariable.defaultValue
|
||||||
|
),
|
||||||
|
booleanValue:
|
||||||
|
configVariable.value ?? configVariable.defaultValue == "true",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,29 +38,38 @@ const AdminConfigInput = ({
|
||||||
{configVariable.type == "string" &&
|
{configVariable.type == "string" &&
|
||||||
(configVariable.obscured ? (
|
(configVariable.obscured ? (
|
||||||
<PasswordInput
|
<PasswordInput
|
||||||
style={{ width: "100%" }}
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
{...form.getInputProps("stringValue")}
|
{...form.getInputProps("stringValue")}
|
||||||
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<TextInput
|
<TextInput
|
||||||
style={{ width: "100%" }}
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
{...form.getInputProps("stringValue")}
|
{...form.getInputProps("stringValue")}
|
||||||
|
placeholder={configVariable.defaultValue}
|
||||||
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{configVariable.type == "text" && (
|
{configVariable.type == "text" && (
|
||||||
<Textarea
|
<Textarea
|
||||||
style={{ width: "100%" }}
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
autosize
|
autosize
|
||||||
{...form.getInputProps("textValue")}
|
{...form.getInputProps("textValue")}
|
||||||
|
placeholder={configVariable.defaultValue}
|
||||||
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
onChange={(e) => onValueChange(configVariable, e.target.value)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{configVariable.type == "number" && (
|
{configVariable.type == "number" && (
|
||||||
<NumberInput
|
<NumberInput
|
||||||
{...form.getInputProps("numberValue")}
|
{...form.getInputProps("numberValue")}
|
||||||
|
placeholder={configVariable.defaultValue}
|
||||||
onChange={(number) => onValueChange(configVariable, number)}
|
onChange={(number) => onValueChange(configVariable, number)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default function AppShellDemo() {
|
||||||
toast.success("Configurations updated successfully");
|
toast.success("Configurations updated successfully");
|
||||||
})
|
})
|
||||||
.catch(toast.axiosError);
|
.catch(toast.axiosError);
|
||||||
config.refresh();
|
void config.refresh();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -75,8 +75,12 @@ export default function AppShellDemo() {
|
||||||
const index = updatedConfigVariables.findIndex(
|
const index = updatedConfigVariables.findIndex(
|
||||||
(item) => item.key === configVariable.key
|
(item) => item.key === configVariable.key
|
||||||
);
|
);
|
||||||
|
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
updatedConfigVariables[index] = configVariable;
|
updatedConfigVariables[index] = {
|
||||||
|
...updatedConfigVariables[index],
|
||||||
|
...configVariable,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
setUpdatedConfigVariables([...updatedConfigVariables, configVariable]);
|
setUpdatedConfigVariables([...updatedConfigVariables, configVariable]);
|
||||||
}
|
}
|
||||||
|
@ -132,9 +136,24 @@ export default function AppShellDemo() {
|
||||||
<Title order={6}>
|
<Title order={6}>
|
||||||
{configVariableToFriendlyName(configVariable.name)}
|
{configVariableToFriendlyName(configVariable.name)}
|
||||||
</Title>
|
</Title>
|
||||||
|
{configVariable.description.split("\n").length == 1 ? (
|
||||||
<Text color="dimmed" size="sm" mb="xs">
|
<Text color="dimmed" size="sm" mb="xs">
|
||||||
{configVariable.description}
|
{configVariable.description}
|
||||||
</Text>
|
</Text>
|
||||||
|
) : (
|
||||||
|
configVariable.description.split("\n").map((line) => (
|
||||||
|
<Text
|
||||||
|
key={line}
|
||||||
|
color="dimmed"
|
||||||
|
size="sm"
|
||||||
|
style={{
|
||||||
|
marginBottom: line === "" ? "1rem" : "0",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{line}
|
||||||
|
</Text>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
<Stack></Stack>
|
<Stack></Stack>
|
||||||
<Box style={{ width: isMobile ? "100%" : "50%" }}>
|
<Box style={{ width: isMobile ? "100%" : "50%" }}>
|
||||||
|
|
|
@ -23,10 +23,12 @@ const get = (key: string, configVariables: Config[]): any => {
|
||||||
|
|
||||||
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
if (!configVariable) throw new Error(`Config variable ${key} not found`);
|
||||||
|
|
||||||
if (configVariable.type == "number") return parseInt(configVariable.value);
|
const value = configVariable.value ?? configVariable.defaultValue;
|
||||||
if (configVariable.type == "boolean") return configVariable.value == "true";
|
|
||||||
|
if (configVariable.type == "number") return parseInt(value);
|
||||||
|
if (configVariable.type == "boolean") return value == "true";
|
||||||
if (configVariable.type == "string" || configVariable.type == "text")
|
if (configVariable.type == "string" || configVariable.type == "text")
|
||||||
return configVariable.value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const finishSetup = async (): Promise<AdminConfig[]> => {
|
const finishSetup = async (): Promise<AdminConfig[]> => {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
type Config = {
|
type Config = {
|
||||||
key: string;
|
key: string;
|
||||||
|
defaultValue: string;
|
||||||
value: string;
|
value: string;
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user