Skip to content

Commit fd96e8b

Browse files
committed
fix(api): cached files are duplicated (same content stored twice)
1 parent f411047 commit fd96e8b

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

Diff for: packages/api/src/index.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import history from 'connect-history-api-fallback';
88
import cors from 'cors';
99
import express from 'express';
1010
import morgan from 'morgan';
11-
import { join } from 'path';
11+
import { join, resolve } from 'path';
1212
import { env } from 'process';
1313

1414
export default function useFork(settings: ForkEnv = env):Promise<{ client: client, fork:Fork }> {
@@ -65,6 +65,26 @@ export default function useFork(settings: ForkEnv = env):Promise<{ client: clien
6565
});
6666
});
6767

68+
// serve the files
69+
app.get('/cache/:folderName/:fileName', (req, res, next) => {
70+
const options = {
71+
root: resolve(fileServer.cacheFolder, req.params.folderName),
72+
dotfiles: 'deny',
73+
headers: {
74+
'x-timestamp': Date.now(),
75+
'x-sent': true,
76+
},
77+
};
78+
const fileName = req.params.fileName;
79+
res.sendFile(fileName, options, (err) => {
80+
if (err) {
81+
next(err);
82+
} else {
83+
fileServer.renew(fileName);
84+
}
85+
});
86+
});
87+
6888
// force authentication for file requests
6989
app.use('/files', (req, res, next) => {
7090
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
@@ -82,6 +102,23 @@ export default function useFork(settings: ForkEnv = env):Promise<{ client: clien
82102
}
83103
});
84104

105+
// force authentication for cache requests
106+
app.use('/cache', (req, res, next) => {
107+
const b64auth = (req.headers.authorization || '').split(' ')[1] || '';
108+
const strauth = Buffer.from(b64auth, 'base64').toString();
109+
const splitIndex = strauth.indexOf(':');
110+
const login = strauth.substring(0, splitIndex);
111+
const password = strauth.substring(splitIndex + 1);
112+
if(login !== env.LOGIN || password !== env.PASSWORD) {
113+
// Access denied...
114+
res.set('WWW-Authenticate', 'Basic realm="401"'); // change this
115+
res.status(401).send('Authentication required.'); // custom message
116+
} else {
117+
// Access granted...
118+
next();
119+
}
120+
});
121+
85122
// serve the view if any
86123
if(env.VIEW) {
87124
app.use(express.static(env.VIEW));

Diff for: packages/api/src/utils/fileserv.ts

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from 'fs';
2-
import { join, resolve } from 'path';
2+
import { basename, dirname, join, resolve } from 'path';
33
import { env } from 'process';
44
import sanitize from 'sanitize-filename';
5+
6+
7+
function* getFiles(dir: string):Generator<string, void, unknown> {
8+
const dirents = readdirSync(dir, { withFileTypes: true });
9+
for (const dirent of dirents.filter(f => f.name !== 'fileserver')) {
10+
const res = resolve(dir, dirent.name);
11+
if (dirent.isDirectory()) {
12+
yield* getFiles(res);
13+
} else {
14+
yield res;
15+
}
16+
}
17+
}
18+
19+
520
export class FileServer {
621
static #instance: FileServer;
722
folder: string;
23+
cacheFolder: string;
824
/** default file's lifetime in seconds */
925
#defaultLifeTime = 60*60*24;
1026
#timeouts: {filename: string, timeout: NodeJS.Timeout}[];
@@ -15,6 +31,7 @@ export class FileServer {
1531
constructor(folder:string) {
1632
if(!env.USER_DATA) throw new Error('USER_DATA not set');
1733
this.folder = resolve(env.USER_DATA, '.cache', folder);
34+
this.cacheFolder = resolve(env.USER_DATA, '.cache');
1835
this.#timeouts = [];
1936
this.setup();
2037
this.empty();
@@ -63,6 +80,10 @@ export class FileServer {
6380
return resolve(this.folder, sanitize(filename));
6481
}
6582

83+
#resolveFilesFromCache() {
84+
return getFiles(this.cacheFolder);
85+
}
86+
6687
#resetFile(filename: string, lifetime: number) {
6788
const path = this.#resolveFile(filename);
6889
const fileExist = existsSync(path);
@@ -101,13 +122,23 @@ export class FileServer {
101122
}
102123
}
103124

125+
#findFromCache(filename: string) {
126+
const files = this.#resolveFilesFromCache();
127+
for(const f of files) {
128+
if(f.includes(sanitize(filename))) return f;
129+
}
130+
}
131+
104132
/**
105133
*
106134
* @param data the data to serv
107135
* @param filename its filename
108136
* @param lifetime how lang is the file available (in seconds)
109137
*/
110138
serv (data: Buffer, filename:string, lifetime = this.#defaultLifeTime) {
139+
const cached = this.#findFromCache(filename);
140+
if(cached) return `/cache/${basename(dirname(cached))}/${filename}`;
141+
111142
const exist = this.#resetFile(filename, lifetime);
112143
if(exist) return `/files/${filename}`;
113144

0 commit comments

Comments
 (0)