feat: allow to use redis cache instead of memory cache (#832)
* feat(backend/cache): allow to use redis cache instead as memory * feat(frontend/admin): add cache section Add a new section for cache attributes. Also add US translation. --------- Co-authored-by: Jules Lefebvre <jules.lefebvre@diabolocom.com>
This commit is contained in:
76
backend/package-lock.json
generated
76
backend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "1.12.0",
|
"version": "1.12.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.787.0",
|
"@aws-sdk/client-s3": "^3.787.0",
|
||||||
|
"@keyv/redis": "^4.4.0",
|
||||||
"@nestjs/cache-manager": "^3.0.1",
|
"@nestjs/cache-manager": "^3.0.1",
|
||||||
"@nestjs/common": "^11.0.17",
|
"@nestjs/common": "^11.0.17",
|
||||||
"@nestjs/config": "^4.0.2",
|
"@nestjs/config": "^4.0.2",
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"cache-manager": "^6.4.2",
|
"cache-manager": "^6.4.2",
|
||||||
|
"cacheable": "^1.9.0",
|
||||||
"clamscan": "^2.4.0",
|
"clamscan": "^2.4.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
@@ -2573,6 +2575,22 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@keyv/redis": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@keyv/redis/-/redis-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-n/KEj3S7crVkoykggqsMUtcjNGvjagGPlJYgO/r6m9hhGZfhp1txJElHxcdJ1ANi/LJoBuOSILj15g6HD2ucqQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@redis/client": "^1.6.0",
|
||||||
|
"cluster-key-slot": "^1.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 18"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"keyv": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@keyv/serialize": {
|
"node_modules/@keyv/serialize": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.0.3.tgz",
|
||||||
@@ -3259,6 +3277,20 @@
|
|||||||
"@prisma/debug": "6.6.0"
|
"@prisma/debug": "6.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@redis/client": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cluster-key-slot": "1.1.2",
|
||||||
|
"generic-pool": "3.9.0",
|
||||||
|
"yallist": "4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@scarf/scarf": {
|
"node_modules/@scarf/scarf": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
|
||||||
@@ -5233,6 +5265,16 @@
|
|||||||
"keyv": "^5.3.2"
|
"keyv": "^5.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cacheable": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-8D5htMCxPDUULux9gFzv30f04Xo3wCnik0oOxKoRTPIBoqA7HtOcJ87uBhQTs3jCfZZTrUBGsYIZOgE0ZRgMAg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"hookified": "^1.8.2",
|
||||||
|
"keyv": "^5.3.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/call-bind-apply-helpers": {
|
"node_modules/call-bind-apply-helpers": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
|
||||||
@@ -5444,6 +5486,15 @@
|
|||||||
"node": ">=0.8"
|
"node": ">=0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cluster-key-slot": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color": {
|
"node_modules/color": {
|
||||||
"version": "4.2.3",
|
"version": "4.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
@@ -6897,6 +6948,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/generic-pool": {
|
||||||
|
"version": "3.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
|
||||||
|
"integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
|
||||||
@@ -7134,6 +7194,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hookified": {
|
||||||
|
"version": "1.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/hookified/-/hookified-1.9.0.tgz",
|
||||||
|
"integrity": "sha512-2yEEGqphImtKIe1NXWEhu6yD3hlFR4Mxk4Mtp3XEyScpSt4pQ4ymmXA1zzxZpj99QkFK+nN0nzjeb2+RUi/6CQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/http-errors": {
|
"node_modules/http-errors": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||||
@@ -7634,9 +7700,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "5.3.2",
|
"version": "5.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-5.3.3.tgz",
|
||||||
"integrity": "sha512-Lji2XRxqqa5Wg+CHLVfFKBImfJZ4pCSccu9eVWK6w4c2SDFLd8JAn1zqTuSFnsxb7ope6rMsnIHfp+eBbRBRZQ==",
|
"integrity": "sha512-Rwu4+nXI9fqcxiEHtbkvoes2X+QfkTRo1TMkPfwzipGsJlJO/z69vqB4FNl9xJ3xCpAcbkvmEabZfPzrwN3+gQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@keyv/serialize": "^1.0.3"
|
"@keyv/serialize": "^1.0.3"
|
||||||
}
|
}
|
||||||
@@ -10855,8 +10922,7 @@
|
|||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.7.1",
|
"version": "2.7.1",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.787.0",
|
"@aws-sdk/client-s3": "^3.787.0",
|
||||||
|
"@keyv/redis": "^4.4.0",
|
||||||
"@nestjs/cache-manager": "^3.0.1",
|
"@nestjs/cache-manager": "^3.0.1",
|
||||||
"@nestjs/common": "^11.0.17",
|
"@nestjs/common": "^11.0.17",
|
||||||
"@nestjs/config": "^4.0.2",
|
"@nestjs/config": "^4.0.2",
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
"argon2": "^0.41.1",
|
"argon2": "^0.41.1",
|
||||||
"body-parser": "^2.2.0",
|
"body-parser": "^2.2.0",
|
||||||
"cache-manager": "^6.4.2",
|
"cache-manager": "^6.4.2",
|
||||||
|
"cacheable": "^1.9.0",
|
||||||
"clamscan": "^2.4.0",
|
"clamscan": "^2.4.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
|
|||||||
@@ -76,6 +76,25 @@ export const configVariables = {
|
|||||||
secret: false,
|
secret: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
cache: {
|
||||||
|
"redis-enabled": {
|
||||||
|
type: "boolean",
|
||||||
|
defaultValue: "false",
|
||||||
|
},
|
||||||
|
"redis-url": {
|
||||||
|
type: "string",
|
||||||
|
defaultValue: "redis://pingvin-redis:6379",
|
||||||
|
secret: true,
|
||||||
|
},
|
||||||
|
ttl: {
|
||||||
|
type: "number",
|
||||||
|
defaultValue: "60",
|
||||||
|
},
|
||||||
|
maxItems: {
|
||||||
|
type: "number",
|
||||||
|
defaultValue: "1000",
|
||||||
|
},
|
||||||
|
},
|
||||||
email: {
|
email: {
|
||||||
enableShareEmailRecipients: {
|
enableShareEmailRecipients: {
|
||||||
type: "boolean",
|
type: "boolean",
|
||||||
@@ -419,11 +438,11 @@ const prisma = new PrismaClient({
|
|||||||
|
|
||||||
async function seedConfigVariables() {
|
async function seedConfigVariables() {
|
||||||
for (const [category, configVariablesOfCategory] of Object.entries(
|
for (const [category, configVariablesOfCategory] of Object.entries(
|
||||||
configVariables
|
configVariables,
|
||||||
)) {
|
)) {
|
||||||
let order = 0;
|
let order = 0;
|
||||||
for (const [name, properties] of Object.entries(
|
for (const [name, properties] of Object.entries(
|
||||||
configVariablesOfCategory
|
configVariablesOfCategory,
|
||||||
)) {
|
)) {
|
||||||
const existingConfigVariable = await prisma.config.findUnique({
|
const existingConfigVariable = await prisma.config.findUnique({
|
||||||
where: { name_category: { name, category } },
|
where: { name_category: { name, category } },
|
||||||
@@ -469,7 +488,7 @@ async function migrateConfigVariables() {
|
|||||||
// Update the config variable if it exists in the seed
|
// Update the config variable if it exists in the seed
|
||||||
} else {
|
} else {
|
||||||
const variableOrder = Object.keys(
|
const variableOrder = Object.keys(
|
||||||
configVariables[existingConfigVariable.category]
|
configVariables[existingConfigVariable.category],
|
||||||
).indexOf(existingConfigVariable.name);
|
).indexOf(existingConfigVariable.name);
|
||||||
await prisma.config.update({
|
await prisma.config.update({
|
||||||
where: {
|
where: {
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { Module } from "@nestjs/common";
|
|||||||
import { ScheduleModule } from "@nestjs/schedule";
|
import { ScheduleModule } from "@nestjs/schedule";
|
||||||
import { AuthModule } from "./auth/auth.module";
|
import { AuthModule } from "./auth/auth.module";
|
||||||
|
|
||||||
import { CacheModule } from "@nestjs/cache-manager";
|
|
||||||
import { APP_GUARD } from "@nestjs/core";
|
import { APP_GUARD } from "@nestjs/core";
|
||||||
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
|
import { ThrottlerGuard, ThrottlerModule } from "@nestjs/throttler";
|
||||||
|
import { AppCacheModule } from "./cache/cache.module";
|
||||||
import { AppController } from "./app.controller";
|
import { AppController } from "./app.controller";
|
||||||
import { ClamScanModule } from "./clamscan/clamscan.module";
|
import { ClamScanModule } from "./clamscan/clamscan.module";
|
||||||
import { ConfigModule } from "./config/config.module";
|
import { ConfigModule } from "./config/config.module";
|
||||||
@@ -38,9 +38,7 @@ import { UserModule } from "./user/user.module";
|
|||||||
ClamScanModule,
|
ClamScanModule,
|
||||||
ReverseShareModule,
|
ReverseShareModule,
|
||||||
OAuthModule,
|
OAuthModule,
|
||||||
CacheModule.register({
|
AppCacheModule,
|
||||||
isGlobal: true,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
41
backend/src/cache/cache.module.ts
vendored
Normal file
41
backend/src/cache/cache.module.ts
vendored
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { CacheModule } from "@nestjs/cache-manager";
|
||||||
|
import { CacheableMemory } from "cacheable";
|
||||||
|
import { createKeyv } from "@keyv/redis";
|
||||||
|
import { Keyv } from "keyv";
|
||||||
|
import { ConfigModule } from "src/config/config.module";
|
||||||
|
import { ConfigService } from "src/config/config.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule,
|
||||||
|
CacheModule.registerAsync({
|
||||||
|
isGlobal: true,
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (configService: ConfigService) => {
|
||||||
|
const useRedis = configService.get("cache.redis-enabled");
|
||||||
|
const ttl = configService.get("cache.ttl");
|
||||||
|
const max = configService.get("cache.maxItems");
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
ttl,
|
||||||
|
max,
|
||||||
|
stores: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (useRedis) {
|
||||||
|
const redisUrl = configService.get("cache.redis-url");
|
||||||
|
config.stores = [
|
||||||
|
new Keyv({ store: new CacheableMemory({ ttl, lruSize: 5000 }) }),
|
||||||
|
createKeyv(redisUrl),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
exports: [CacheModule],
|
||||||
|
})
|
||||||
|
export class AppCacheModule {}
|
||||||
@@ -23,12 +23,21 @@ share:
|
|||||||
shareIdLength: "8"
|
shareIdLength: "8"
|
||||||
#Maximum share size
|
#Maximum share size
|
||||||
maxSize: "1000000000"
|
maxSize: "1000000000"
|
||||||
#Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression.
|
#Adjust the level to balance between file size and compression speed. Valid values range from 0 to 9, with 0 being no compression and 9 being maximum compression.
|
||||||
zipCompressionLevel: "9"
|
zipCompressionLevel: "9"
|
||||||
#Adjust the chunk size for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.
|
#Adjust the chunk size for your uploads to balance efficiency and reliability according to your internet connection. Smaller chunks can enhance success rates for unstable connections, while larger chunks make uploads faster for stable connections.
|
||||||
chunkSize: "10000000"
|
chunkSize: "10000000"
|
||||||
#The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.
|
#The share creation modal automatically appears when a user selects files, eliminating the need to manually click the button.
|
||||||
autoOpenShareModal: "false"
|
autoOpenShareModal: "false"
|
||||||
|
cache:
|
||||||
|
#Normally Pingvin Share caches information in memory. If you run multiple instances of Pingvin Share, you need to enable Redis caching to share the cache between the instances.
|
||||||
|
redis-enabled: "false"
|
||||||
|
#Url to connect to the Redis instance used for caching.
|
||||||
|
redis-url: redis://pingvin-redis:6379
|
||||||
|
#Time in second to keep information inside the cache.
|
||||||
|
ttl: "60"
|
||||||
|
#Maximum number of items inside the cache.
|
||||||
|
maxItems: "1000"
|
||||||
email:
|
email:
|
||||||
#Whether to allow email sharing with recipients. Only enable this if SMTP is activated.
|
#Whether to allow email sharing with recipients. Only enable this if SMTP is activated.
|
||||||
enableShareEmailRecipients: "false"
|
enableShareEmailRecipients: "false"
|
||||||
|
|||||||
@@ -13,13 +13,14 @@ import Link from "next/link";
|
|||||||
import { Dispatch, SetStateAction } from "react";
|
import { Dispatch, SetStateAction } from "react";
|
||||||
import {
|
import {
|
||||||
TbAt,
|
TbAt,
|
||||||
|
TbBinaryTree,
|
||||||
|
TbBucket,
|
||||||
TbMail,
|
TbMail,
|
||||||
|
TbScale,
|
||||||
|
TbServerBolt,
|
||||||
|
TbSettings,
|
||||||
TbShare,
|
TbShare,
|
||||||
TbSocial,
|
TbSocial,
|
||||||
TbBucket,
|
|
||||||
TbBinaryTree,
|
|
||||||
TbSettings,
|
|
||||||
TbScale,
|
|
||||||
} from "react-icons/tb";
|
} from "react-icons/tb";
|
||||||
import { FormattedMessage } from "react-intl";
|
import { FormattedMessage } from "react-intl";
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ const categories = [
|
|||||||
{ name: "LDAP", icon: <TbBinaryTree /> },
|
{ name: "LDAP", icon: <TbBinaryTree /> },
|
||||||
{ name: "S3", icon: <TbBucket /> },
|
{ name: "S3", icon: <TbBucket /> },
|
||||||
{ name: "Legal", icon: <TbScale /> },
|
{ name: "Legal", icon: <TbScale /> },
|
||||||
|
{ name: "Cache", icon: <TbServerBolt /> },
|
||||||
];
|
];
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
const useStyles = createStyles((theme) => ({
|
||||||
|
|||||||
@@ -423,6 +423,7 @@ export default {
|
|||||||
"admin.config.title": "Configuration",
|
"admin.config.title": "Configuration",
|
||||||
"admin.config.category.general": "General",
|
"admin.config.category.general": "General",
|
||||||
"admin.config.category.share": "Share",
|
"admin.config.category.share": "Share",
|
||||||
|
"admin.config.category.cache": "Cache",
|
||||||
"admin.config.category.email": "Email",
|
"admin.config.category.email": "Email",
|
||||||
"admin.config.category.smtp": "SMTP",
|
"admin.config.category.smtp": "SMTP",
|
||||||
"admin.config.category.oauth": "Social Login",
|
"admin.config.category.oauth": "Social Login",
|
||||||
@@ -446,6 +447,19 @@ export default {
|
|||||||
"Change your logo by uploading a new image. The image must be a PNG and should have the format 1:1.",
|
"Change your logo by uploading a new image. The image must be a PNG and should have the format 1:1.",
|
||||||
"admin.config.general.logo.placeholder": "Pick image",
|
"admin.config.general.logo.placeholder": "Pick image",
|
||||||
|
|
||||||
|
"admin.config.cache.ttl": "TTL",
|
||||||
|
"admin.config.cache.ttl.description":
|
||||||
|
"Time in second to keep information inside the cache.",
|
||||||
|
"admin.config.cache.max-items": "Maximum items",
|
||||||
|
"admin.config.cache.max-items.description":
|
||||||
|
"Maximum number of items inside the cache.",
|
||||||
|
"admin.config.cache.redis-enabled": "Redis enabled",
|
||||||
|
"admin.config.cache.redis-enabled.description":
|
||||||
|
"Normally Pingvin Share caches information in memory. If you run multiple instances of Pingvin Share, you need to enable Redis caching to share the cache between the instances.",
|
||||||
|
"admin.config.cache.redis-url": "Redis URL",
|
||||||
|
"admin.config.cache.redis-url.description":
|
||||||
|
"Url to connect to the Redis instance used for caching.",
|
||||||
|
|
||||||
"admin.config.email.enable-share-email-recipients":
|
"admin.config.email.enable-share-email-recipients":
|
||||||
"Enable email recipient sharing",
|
"Enable email recipient sharing",
|
||||||
"admin.config.email.enable-share-email-recipients.description":
|
"admin.config.email.enable-share-email-recipients.description":
|
||||||
|
|||||||
Reference in New Issue
Block a user