diff --git a/README.md b/README.md
index c2f029dbb..728a7fbaf 100644
--- a/README.md
+++ b/README.md
@@ -228,6 +228,22 @@ A non-comprehensive list of things to look out during development and review:
### Integrations
+#### Redis
+
+Redis is used to store sessions and caches in production.
+To run it locally, start the container in `docker/docker-compose.yml':
+
+```bash
+$ cd docker
+$ docker compose up
+```
+
+If you need to inspect redis via `redis-cli` you can use:
+
+```bash
+$ docker compose run --interactive --tty --rm redis redis-cli -h redis
+```
+
#### AWS
If you want to test with AWS, you can fill in all the `AWS_*` values with your own credentials in `.env.dist`.
@@ -255,7 +271,11 @@ Due to how [caches in Nitro](https://nitro.build/guide/cache) work, they automat
when their function changes. However, this heuristic does not cover other functions the cached function depends on.
You can manually clear caches:
```bash
-rm -rf .nuxt/cache
+# when using the filesystem
+$ rm -rf .nuxt/cache
+# when using redis
+$ cd docker
+$ docker compose exec redis sh -c "redis-cli KEYS \"cache:*\" | xargs -n 100 redis-cli DEL"
```
#### Module did not self-register
diff --git a/components/admin/AdminStorageItem.vue b/components/admin/AdminStorageItem.vue
index 9c65d2a0c..af5992ab6 100644
--- a/components/admin/AdminStorageItem.vue
+++ b/components/admin/AdminStorageItem.vue
@@ -54,8 +54,7 @@ const remove = async () => {
-
-
+
props.itemKey.replace(/^.+:/, ''));
+const isOpen = ref(false);
+
const sumSize = (tree: Tree): number => {
return [...tree.values()].map((node) => {
if (node instanceof Map) {
@@ -22,7 +24,7 @@ const sumSize = (tree: Tree): number => {
-
+
{{ chunk }}
@@ -31,7 +33,7 @@ const sumSize = (tree: Tree): number => {
-
+
=6'}
- memorystore@1.6.7:
- resolution: {integrity: sha512-OZnmNY/NDrKohPQ+hxp0muBcBKrzKNtHr55DbqSx9hLsYVNnomSAMRAtI7R64t3gf3ID7tHQA7mG4oL3Hu9hdw==}
- engines: {node: '>=0.10'}
-
meow@3.7.0:
resolution: {integrity: sha512-TNdwZs0skRlpPpCUK25StC4VH+tP5GgeY1HQOOGP+lQ2xtdkN2VtT/5tiX9k3IWpkBPV9b3LsAWXn4GGi/PrSA==}
engines: {node: '>=0.10.0'}
@@ -6451,9 +6444,6 @@ packages:
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
- pseudomap@1.0.2:
- resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
-
psl@1.9.0:
resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
@@ -8098,9 +8088,6 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
- yallist@2.1.2:
- resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==}
-
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -14535,11 +14522,6 @@ snapshots:
lru-cache@10.4.3: {}
- lru-cache@4.1.5:
- dependencies:
- pseudomap: 1.0.2
- yallist: 2.1.2
-
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
@@ -14651,13 +14633,6 @@ snapshots:
mimic-fn: 2.1.0
p-is-promise: 2.1.0
- memorystore@1.6.7:
- dependencies:
- debug: 4.4.0(supports-color@9.4.0)
- lru-cache: 4.1.5
- transitivePeerDependencies:
- - supports-color
-
meow@3.7.0:
dependencies:
camelcase-keys: 2.1.0
@@ -15779,8 +15754,6 @@ snapshots:
proxy-from-env@1.1.0: {}
- pseudomap@1.0.2: {}
-
psl@1.9.0: {}
pump@3.0.2:
@@ -17658,8 +17631,6 @@ snapshots:
y18n@5.0.8: {}
- yallist@2.1.2: {}
-
yallist@3.1.1: {}
yallist@4.0.0: {}
diff --git a/server/index.ts b/server/index.ts
index 69edef19e..3c8eb6d2d 100644
--- a/server/index.ts
+++ b/server/index.ts
@@ -6,7 +6,6 @@ import session from 'express-session';
import grant from 'grant';
import { useBase } from 'h3';
import { defineExpressHandler, getH3Event } from 'h3-express';
-import memorystore from 'memorystore';
import SQL from 'sql-template-strings';
import buildLocaleList from '../src/buildLocaleList.ts';
@@ -43,7 +42,26 @@ import { config } from './social.ts';
import { closeAuditLogConnection } from '~/server/audit.ts';
import { getLocale } from '~/server/data.ts';
-const MemoryStore = memorystore(session);
+class StorageStore extends session.Store {
+ get(sid: string, callback: (err: unknown, session?: (session.SessionData | null)) => void): void {
+ useStorage('session').getItem(sid)
+ .then((session) => callback(null, session))
+ .catch((error) => callback(error));
+ }
+
+ set(sid: string, session: session.SessionData, callback?: (err?: unknown) => void): void {
+ // unwrap session to make it a primitive object (otherwise unstorage will reject serializing the object)
+ useStorage('session').setItem(sid, { ...session }, { ttl: 24 * 60 * 60 })
+ .then(() => callback?.())
+ .catch((error) => callback?.(error));
+ }
+
+ destroy(sid: string, callback?: (err?: unknown) => void): void {
+ useStorage('session').removeItem(sid)
+ .then(() => callback?.())
+ .catch((error) => callback?.(error));
+ }
+}
const router = express.Router();
@@ -52,9 +70,7 @@ router.use(session({
cookie: { ...longtimeCookieSetting, sameSite: undefined }, // somehow, sameSite=lax breaks sign-in with apple 🙄
resave: false,
saveUninitialized: false,
- store: new MemoryStore({
- checkPeriod: 86400000, // 24h
- }),
+ store: new StorageStore(),
}));
export class LazyDatabase implements Database {
diff --git a/server/plugins/storage.ts b/server/plugins/storage.ts
index 4b0bc6a3a..f687f56b4 100644
--- a/server/plugins/storage.ts
+++ b/server/plugins/storage.ts
@@ -1,9 +1,15 @@
-import { defineNitroPlugin, useStorage } from 'nitropack/runtime';
-import fsLiteDriver from 'unstorage/drivers/fs-lite';
+import redisDriver from 'unstorage/drivers/redis';
-export default defineNitroPlugin(async () => {
- useStorage().mount('calendar', fsLiteDriver({ base: 'calendar' }));
- // should be resolved when Nitro releases a nev version https://github.com/nitrojs/nitro/issues/3017
- await useStorage().unmount('data');
- useStorage().mount('data', fsLiteDriver({ base: '.data/kv' }));
+export default defineNitroPlugin(() => {
+ const runtimeConfig = useRuntimeConfig();
+ const storage = useStorage();
+
+ if (!import.meta.dev) {
+ storage.mount('cache', redisDriver({
+ base: `${runtimeConfig.public.env}:cache`,
+ }));
+ storage.mount('session', redisDriver({
+ base: `${runtimeConfig.public.env}:session`,
+ }));
+ }
});