Skip to content

Commit

Permalink
feat(share, config): more variables, placeholder and reset default (#132
Browse files Browse the repository at this point in the history
)

* 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>
  • Loading branch information
iUnstable0 and stonith404 committed Mar 23, 2023
1 parent a0d1d98 commit beece56
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 73 deletions.
@@ -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;
19 changes: 10 additions & 9 deletions backend/prisma/schema.prisma
Expand Up @@ -131,15 +131,16 @@ model ShareSecurity {
model Config {
updatedAt DateTime @updatedAt
name String
category String
type String
value String
description String
obscured Boolean @default(false)
secret Boolean @default(true)
locked Boolean @default(false)
order Int
name String
category String
type String
defaultValue String @default("")
value String?
description String
obscured Boolean @default(false)
secret Boolean @default(true)
locked Boolean @default(false)
order Int
@@id([name, category])
}
54 changes: 27 additions & 27 deletions backend/prisma/seed/config.seed.ts
Expand Up @@ -6,50 +6,50 @@ const configVariables: ConfigVariables = {
jwtSecret: {
description: "Long random string used to sign JWT tokens",
type: "string",
value: crypto.randomBytes(256).toString("base64"),
defaultValue: crypto.randomBytes(256).toString("base64"),
locked: true,
},
},
general: {
appName: {
description: "Name of the application",
type: "string",
value: "Pingvin Share",
defaultValue: "Pingvin Share",
secret: false,
},
appUrl: {
description: "On which URL Pingvin Share is available",
type: "string",
value: "http://localhost:3000",
defaultValue: "http://localhost:3000",

secret: false,
},
showHomePage: {
description: "Whether to show the home page",
type: "boolean",
value: "true",
defaultValue: "true",
secret: false,
},
},
share: {
allowRegistration: {
description: "Whether registration is allowed",
type: "boolean",
value: "true",
defaultValue: "true",

secret: false,
},
allowUnauthenticatedShares: {
description: "Whether unauthorized users can create shares",
type: "boolean",
value: "false",
defaultValue: "false",

secret: false,
},
maxSize: {
description: "Maximum share size in bytes",
type: "number",
value: "1073741824",
defaultValue: "1073741824",

secret: false,
},
Expand All @@ -59,95 +59,95 @@ const configVariables: ConfigVariables = {
description:
"Whether to allow emails to share recipients. Only enable this if you have enabled SMTP.",
type: "boolean",
value: "false",
defaultValue: "false",

secret: false,
},
shareRecipientsSubject: {
description:
"Subject of the email which gets sent to the share recipients.",
type: "string",
value: "Files shared with you",
defaultValue: "Files shared with you",
},
shareRecipientsMessage: {
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",
value:
"Hey!\n{creator} shared some files with you. View or download the files with this link: {shareUrl}\nShared securely with Pingvin Share 🐧",
defaultValue:
"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: {
description:
"Subject of the email which gets sent when someone created a share with your reverse share link.",
type: "string",
value: "Reverse share link used",
defaultValue: "Reverse share link used",
},
reverseShareMessage: {
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.",
type: "text",
value:
"Hey!\nA share was just created with your reverse share link: {shareUrl}\nShared securely with Pingvin Share 🐧",
defaultValue:
"Hey!\n\nA share was just created with your reverse share link: {shareUrl}\n\nShared securely with Pingvin Share 🐧",
},
resetPasswordSubject: {
description:
"Subject of the email which gets sent when a user requests a password reset.",
type: "string",
value: "Pingvin Share password reset",
defaultValue: "Pingvin Share password reset",
},
resetPasswordMessage: {
description:
"Message which gets sent when a user requests a password reset. {url} will be replaced with the reset password URL.",
type: "text",
value:
"Hey!\nYou requested a password reset. Click this link to reset your password: {url}\nThe link expires in a hour.\nPingvin Share 🐧",
defaultValue:
"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: {
description:
"Subject of the email which gets sent when an admin invites an user.",
type: "string",
value: "Pingvin Share invite",
defaultValue: "Pingvin Share invite",
},
inviteMessage: {
description:
"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",
value:
"Hey!\nYou were invited to Pingvin Share. Click this link to accept the invite: {url}\nYour password is: {password}\nPingvin Share 🐧",
defaultValue:
"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: {
enabled: {
description:
"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",
value: "false",
defaultValue: "false",
secret: false,
},
host: {
description: "Host of the SMTP server",
type: "string",
value: "",
defaultValue: "",
},
port: {
description: "Port of the SMTP server",
type: "number",
value: "0",
defaultValue: "0",
},
email: {
description: "Email address which the emails get sent from",
type: "string",
value: "",
defaultValue: "",
},
username: {
description: "Username of the SMTP server",
type: "string",
value: "",
defaultValue: "",
},
password: {
description: "Password of the SMTP server",
type: "string",
value: "",
defaultValue: "",
obscured: true,
},
},
Expand Down
20 changes: 13 additions & 7 deletions backend/src/config/config.service.ts
Expand Up @@ -21,10 +21,12 @@ export class ConfigService {

if (!configVariable) throw new Error(`Config variable ${key} not found`);

if (configVariable.type == "number") return parseInt(configVariable.value);
if (configVariable.type == "boolean") return configVariable.value == "true";
const value = configVariable.value ?? configVariable.defaultValue;

if (configVariable.type == "number") return parseInt(value);
if (configVariable.type == "boolean") return value == "true";
if (configVariable.type == "string" || configVariable.type == "text")
return configVariable.value;
return value;
}

async getByCategory(category: string) {
Expand All @@ -35,8 +37,9 @@ export class ConfigService {

return configVariables.map((variable) => {
return {
key: `${variable.category}.${variable.name}`,
...variable,
key: `${variable.category}.${variable.name}`,
value: variable.value ?? variable.defaultValue,
};
});
}
Expand All @@ -48,8 +51,9 @@ export class ConfigService {

return configVariables.map((variable) => {
return {
key: `${variable.category}.${variable.name}`,
...variable,
key: `${variable.category}.${variable.name}`,
value: variable.value ?? variable.defaultValue,
};
});
}
Expand Down Expand Up @@ -77,7 +81,9 @@ export class ConfigService {
if (!configVariable || configVariable.locked)
throw new NotFoundException("Config variable not found");

if (
if (value == "") {
value = null;
} else if (
typeof value != configVariable.type &&
typeof value == "string" &&
configVariable.type != "text"
Expand All @@ -94,7 +100,7 @@ export class ConfigService {
name: key.split(".")[1],
},
},
data: { value: value.toString() },
data: { value: value ? value.toString() : null },
});

this.configVariables = await this.prisma.config.findMany();
Expand Down
3 changes: 3 additions & 0 deletions backend/src/config/dto/adminConfig.dto.ts
Expand Up @@ -8,6 +8,9 @@ export class AdminConfigDTO extends ConfigDTO {
@Expose()
secret: boolean;

@Expose()
defaultValue: string;

@Expose()
updatedAt: Date;

Expand Down
3 changes: 1 addition & 2 deletions backend/src/config/dto/updateConfig.dto.ts
@@ -1,11 +1,10 @@
import { IsNotEmpty, IsString, ValidateIf } from "class-validator";
import { IsNotEmpty, IsString } from "class-validator";

class UpdateConfigDTO {
@IsString()
key: string;

@IsNotEmpty()
@ValidateIf((dto) => dto.value !== "")
value: string | number | boolean;
}

Expand Down
14 changes: 12 additions & 2 deletions backend/src/email/email.service.ts
Expand Up @@ -4,6 +4,7 @@ import {
Logger,
} from "@nestjs/common";
import { User } from "@prisma/client";
import * as moment from "moment";
import * as nodemailer from "nodemailer";
import { ConfigService } from "src/config/config.service";

Expand Down Expand Up @@ -43,10 +44,12 @@ export class EmailService {
});
}

async sendMailToShareRecepients(
async sendMailToShareRecipients(
recipientEmail: string,
shareId: string,
creator?: User
creator?: User,
description?: string,
expiration?: Date
) {
if (!this.config.get("email.enableShareEmailRecipients"))
throw new InternalServerErrorException("Email service disabled");
Expand All @@ -61,6 +64,13 @@ export class EmailService {
.replaceAll("\\n", "\n")
.replaceAll("{creator}", creator?.username ?? "Someone")
.replaceAll("{shareUrl}", shareUrl)
.replaceAll("{desc}", description ?? "No description")
.replaceAll(
"{expires}",
moment(expiration).unix() != 0
? moment(expiration).fromNow()
: "in: never"
)
);
}

Expand Down

0 comments on commit beece56

Please sign in to comment.