Compare commits

...

51 commits

Author SHA1 Message Date
Amelia Yukii
eece498288 merge: Built-in video thumbnail generator (for remote video thumbnails without needing "Cache remote media" enabled) (!310)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/310
2024-04-11 21:07:36 +00:00
dakkar
e0afeff248 merge: hide images/videos in og cards, when under a CW - fixes #487 (!488)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/488

Closes #487

Approved-by: Marie <marie@kaifa.ch>
Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-04-11 20:40:38 +00:00
Marie
cfc8081cec merge: bump tmp@0.2.3 - fixes #464 (!475)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/475

Closes #464

Approved-by: Marie <marie@kaifa.ch>
Approved-by: Luna <her@mint.lgbt>
Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-04-11 18:00:40 +00:00
Marie
011ccd3a9a merge: bump devel version (!486)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/486

Approved-by: Marie <marie@kaifa.ch>
Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-04-11 17:21:32 +00:00
Amelia Yukii
28065fc1d1 merge: handle ranged requests for proxied files - fixes #494 (!490)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/490

Closes #494

Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
Approved-by: Marie <marie@kaifa.ch>
2024-04-11 10:07:25 +00:00
dakkar
960f4fcff7 detect size of remote files - fixes #494
without this, remote files are assumed to have size 0 (even if we just
downloaded them!) and the range-related code won't run
2024-04-09 16:21:30 +01:00
dakkar
92eec2178f return 206 for every ranged response - fixes #494 2024-04-09 15:42:29 +01:00
dakkar
56dca6dbf5 hide images/videos in og cards, when under a CW - fixes #487 2024-04-07 16:58:13 +01:00
dakkar
2a634e0309 bump devel version 2024-03-30 12:48:03 +00:00
dakkar
e6970a0e7c Merge branch 'stable' into bump-devel-version 2024-03-30 12:44:31 +00:00
Amelia Yukii
571272a564 merge: release 2024.3.2 (!485)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/485

Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-03-30 11:19:08 +00:00
dakkar
30bb0f60a2 version bump 2024-03-30 11:09:00 +00:00
dakkar
328546c4cd Merge branch 'develop' into release/2024-03-30 2024-03-30 11:08:26 +00:00
dakkar
f4e89f2e6b bump tmp@0.2.3 - fixes #464
see also https://github.com/raszi/node-tmp/issues/295
2024-03-19 17:13:43 +00:00
dakkar
2cad97c1ab merge: release 2024.3.1 (!449)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/449

Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
Approved-by: Marie <marie@kaifa.ch>
2024-03-02 17:43:24 +00:00
dakkar
6ecfe7c7c3 remove duplicate method 2024-03-02 17:34:31 +00:00
dakkar
23f476dbf3 Merge branch 'develop' into release/2024.3.1 2024-03-02 17:28:34 +00:00
Amelia Yukii
7a1251423f merge: Add missing IMPORTANT_NOTES.md from Sharkey/OldJoinSharkey (!443)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/443

Approved-by: dakkar <dakkar@thenautilus.net>
Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-02-24 18:20:48 +00:00
Marie
7f5492a395 Add missing IMPORTANT_NOTES.md from Sharkey/OldJoinSharkey 2024-02-24 18:20:48 +00:00
Amelia Yukii
11d9fd9199 merge: import upstream ssrf fix on our stable (!425)
View MR for information: https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/425

Approved-by: Leah <kevinlukej@gmail.com>
Approved-by: Amelia Yukii <amelia.yukii@shourai.de>
2024-02-17 13:06:47 +00:00
syuilo
6132bc3b3e fix of 9a70ce8f5e
Co-Authored-By: RyotaK <49341894+Ry0taK@users.noreply.github.com>
2024-02-17 12:54:45 +00:00
dakkar
fef7a7b99a bump version 2024-02-17 12:38:01 +00:00
tamaina
1948ca9aa8 Merge pull request from GHSA-qqrm-9grj-6v32 2024-02-17 12:36:44 +00:00
Amelia Yukii
848e1f9a56 version is better
(cherry picked from commit fb455e4fd9)
2024-02-01 16:11:48 +00:00
Amelia Yukii
9c4353ee79 Update .gitlab-ci.yml
(cherry picked from commit 8c5818acf0)
2024-02-01 16:10:47 +00:00
Amelia Yukii
a6e257f502 Merge branch 'feture/code-injection-fix' into 'develop'
CVE: Fixed code injection from twitter import

See merge request TransFem-org/Sharkey!390

(cherry picked from commit 127f8556d4)

2a8e93e4 Fixed code injection from twitter import
2024-02-01 15:07:35 +00:00
Amelia Yukii
310e1a1262 Merge branch 'Amelia-stable-patch-29368' into 'stable'
Update docker-compose_example.yml

See merge request TransFem-org/Sharkey!389
2024-02-01 14:44:14 +00:00
Amelia Yukii
15f3c046d1 Update docker-compose_example.yml 2024-02-01 14:42:19 +00:00
Amelia Yukii
01d695428a Revert "build stable with stable tag"
This reverts commit acf3e3460f
2024-02-01 14:15:10 +00:00
Amelia Yukii
acf3e3460f build stable with stable tag 2024-02-01 14:00:56 +00:00
Amelia Yukii
4c8116859c Revert "Merge branch 'cherry-pick-3b2d47b1' into 'stable'"
This reverts merge request !386
2024-02-01 13:55:44 +00:00
Amelia Yukii
0e13397db7 Merge branch 'cherry-pick-3b2d47b1' into 'stable'
build stable with stable tag

See merge request TransFem-org/Sharkey!386
2024-02-01 13:41:34 +00:00
Amelia Yukii
ad8818508f Update file .gitlab-ci.yml
(cherry picked from commit 3b2d47b1e3)
2024-02-01 13:38:19 +00:00
Amelia Yukii
d444ee662f Merge branch 'cherry-pick-522ab39d' into 'stable'
Merge branch 'gitlab-ci' into 'develop'

See merge request TransFem-org/Sharkey!383
2024-02-01 10:23:23 +00:00
Amelia Yukii
4c354fff2d Merge branch 'gitlab-ci' into 'develop' 2024-02-01 10:23:23 +00:00
ShittyKopper
634259d600 upd: add config option to enable builtin video thumbnail generator 2024-01-05 13:35:53 +03:00
ShittyKopper
a3c302e756 upd: introduce a separate ./cache dir and cache video thumbnails there 2024-01-05 13:35:53 +03:00
ShittyKopper
bc24e6a294 upd: use builtin thumbnail generator if no thumbnail generator is set 2024-01-05 13:35:42 +03:00
ShittyKopper
1cd59c1ee3 feat: initial builtin video thumbnail generator implementation
when you disable "cache external media", video thumbnails off of remote
instances do not get generated. misskey has a videoThumbnailGenerator config
option to point to an external service to make that happen, but they do
not provide any kind of implementation (or any documentation beyond a
comment on the config file)

this provides a video thumbnail generator that uses the same thumbnail
generation code path used in local files, providing a quick and dirty
solution to instances that want video thumbnails without the need to store
external media permanently

the eventual goal of this is to be the fallback implementation when that
config option is unset.

the current implementation is extremely bare bones and performs no caching
or any other optimizations whatsoever
2024-01-04 20:55:12 +03:00
Marie
b81448edf6 merge: release 2023.12.0 2023-12-31 23:19:41 +01:00
Marie
134d2895f0 fix: merge conflict 2023-12-31 23:11:15 +01:00
Marie
7ba8fde9b9 chore: change version 2023-12-31 22:49:43 +01:00
Marie
1022280465
release: 2023.11.2 2023-12-01 00:01:19 +01:00
Marie
021d3924e6
chore: change version 2023-11-30 23:57:04 +01:00
Mar0xy
b6d50d781f
Merge branch 'stable' of https://github.com/transfem-org/Sharkey into stable 2023-11-26 18:47:44 +01:00
Mar0xy
1d411bb885
chore: fix locales 2023-11-26 18:47:20 +01:00
Marie
f7afd1ae4a
release: 2023.11.1 2023-11-26 17:28:42 +01:00
Marie
1ef1f2a03c
Merge branch 'stable' into release/2023.11.1 2023-11-26 17:26:30 +01:00
Marie
829ce4f86a
merge: 2023.11.0 2023-11-07 20:16:20 +01:00
Mar0xy
6d5d863150
merge: last minute changes 2023-11-07 20:07:53 +01:00
Marie
fc7d4bc420
chore: set release version 2023-11-07 19:39:18 +01:00
14 changed files with 144 additions and 12 deletions

View file

@ -255,6 +255,11 @@ proxyRemoteFiles: true
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
#videoThumbnailGenerator: https://example.com
# Enables the built-in thumbnail generator for remote videos. (default: false)
# Only useful if "Cache remote files" is disabled, and "videoThumbnailGenerator" is unset.
# Without it, remote video files that are not cached will not have any thumbnails.
#enableBuiltinVideoThumbnailGenerator: false
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
# check that inbound ActivityPub GET requests are signed ("authorized fetch")

View file

@ -270,6 +270,11 @@ proxyRemoteFiles: true
# https://example.com/thumbnail.webp?thumbnail=1&url=https%3A%2F%2Fstorage.example.com%2Fpath%2Fto%2Fvideo.mp4
#videoThumbnailGenerator: https://example.com
# Enables the built-in thumbnail generator for remote videos. (default: false)
# Only useful if "Cache remote files" is disabled, and "videoThumbnailGenerator" is unset.
# Without it, remote video files that are not cached will not have any thumbnails.
#enableBuiltinVideoThumbnailGenerator: false
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
# check that inbound ActivityPub GET requests are signed ("authorized fetch")

1
.gitignore vendored
View file

@ -55,6 +55,7 @@ api-docs.json
*.code-workspace
.DS_Store
/files
/cache
ormconfig.json
temp
/packages/frontend/src/**/*.stories.ts

View file

@ -55,6 +55,8 @@ getImageTag:
only:
- stable
- develop
- tags
buildDocker:
stage: deploy
needs:
@ -78,6 +80,8 @@ buildDocker:
only:
- stable
- develop
- tags
mergeManifests:
stage: deploy
needs:
@ -103,3 +107,4 @@ mergeManifests:
only:
- stable
- develop
- tags

View file

@ -21,6 +21,7 @@ services:
- shonk
volumes:
- ./files:/sharkey/files
- ./cache:/sharkey/cache
- ./.config:/sharkey/.config:ro
redis:

View file

@ -1,6 +1,6 @@
{
"name": "sharkey",
"version": "2024.3.1",
"version": "2024.3.2-devel",
"codename": "shonk",
"repository": {
"type": "git",

View file

@ -172,7 +172,7 @@
"stringz": "2.1.0",
"systeminformation": "5.22.0",
"tinycolor2": "1.6.0",
"tmp": "0.2.2",
"tmp": "0.2.3",
"tsc-alias": "1.8.8",
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.20",

View file

@ -88,6 +88,7 @@ type Source = {
mediaProxy?: string;
proxyRemoteFiles?: boolean;
videoThumbnailGenerator?: string;
enableBuiltinVideoThumbnailGenerator?: boolean;
customMOTD?: string[];
@ -170,6 +171,7 @@ export type Config = {
mediaProxy: string;
externalMediaProxyEnabled: boolean;
videoThumbnailGenerator: string | null;
enableBuiltinVideoThumbnailGenerator: boolean;
redis: RedisOptions & RedisOptionsSource;
redisForPubsub: RedisOptions & RedisOptionsSource;
redisForJobQueue: RedisOptions & RedisOptionsSource;
@ -276,6 +278,7 @@ export function loadConfig(): Config {
videoThumbnailGenerator: config.videoThumbnailGenerator ?
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
: null,
enableBuiltinVideoThumbnailGenerator: config.enableBuiltinVideoThumbnailGenerator ?? false,
userAgent: `Misskey/${version} (${config.url})`,
clientEntry: clientManifest['src/_boot_.ts'],
clientManifestExists: clientManifestExists,

View file

@ -16,6 +16,7 @@ const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const path = Path.resolve(_dirname, '../../../../files');
const cachePath = Path.resolve(_dirname, '../../../../cache');
@Injectable()
export class InternalStorageService {
@ -30,11 +31,26 @@ export class InternalStorageService {
return Path.resolve(path, key);
}
@bindThis
public resolveCachePath(key: string) {
return Path.resolve(cachePath, key);
}
@bindThis
public existsCache(key: string) {
return fs.existsSync(this.resolveCachePath(key));
}
@bindThis
public read(key: string) {
return fs.createReadStream(this.resolvePath(key));
}
@bindThis
public readCache(key: string) {
return fs.createReadStream(this.resolveCachePath(key));
}
@bindThis
public saveFromPath(key: string, srcPath: string) {
fs.mkdirSync(path, { recursive: true });
@ -49,8 +65,19 @@ export class InternalStorageService {
return `${this.config.url}/files/${key}`;
}
@bindThis
public saveCacheFromBuffer(key: string, data: Buffer) {
fs.mkdirSync(cachePath, { recursive: true });
fs.writeFileSync(this.resolveCachePath(key), data);
}
@bindThis
public del(key: string) {
fs.unlink(this.resolvePath(key), () => {});
}
@bindThis
public delCache(key: string) {
fs.unlink(this.resolveCachePath(key), () => {});
}
}

View file

@ -50,7 +50,16 @@ export class VideoProcessingService {
@bindThis
public getExternalVideoThumbnailUrl(url: string): string | null {
if (this.config.videoThumbnailGenerator == null) return null;
if (this.config.videoThumbnailGenerator == null) {
if (this.config.enableBuiltinVideoThumbnailGenerator) {
return appendQuery(
`${this.config.url}/proxy/thumbnail.webp`,
query({ url }),
);
}
return null;
}
return appendQuery(
`${this.config.videoThumbnailGenerator}/thumbnail.webp`,

View file

@ -4,6 +4,7 @@
*/
import * as fs from 'node:fs';
import * as crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
@ -34,6 +35,7 @@ const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const assets = `${_dirname}/../../server/file/assets/`;
const cacheDir = `${_dirname}/../../../../cache/`;
@Injectable()
export class FileServerService {
@ -85,6 +87,15 @@ export class FileServerService {
done();
});
if (this.config.enableBuiltinVideoThumbnailGenerator) {
fastify.get<{
Querystring: { url: string; };
}>('/proxy/thumbnail.webp', async (request, reply) => {
return await this.videoThumbnailHandler(request, reply)
.catch(err => this.errorHandler(request, reply, err));
});
}
fastify.get<{
Params: { url: string; };
Querystring: { url?: string; };
@ -192,6 +203,7 @@ export class FileServerService {
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
} else {
image = {
data: fs.createReadStream(file.path),
@ -261,7 +273,6 @@ export class FileServerService {
const parts = range.replace(/bytes=/, '').split('-');
const start = parseInt(parts[0], 10);
let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1;
console.log(end);
if (end > file.file.size) {
end = file.file.size - 1;
}
@ -431,6 +442,7 @@ export class FileServerService {
reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`);
reply.header('Accept-Ranges', 'bytes');
reply.header('Content-Length', chunksize);
reply.code(206);
} else {
image = {
data: fs.createReadStream(file.path),
@ -466,6 +478,61 @@ export class FileServerService {
}
}
@bindThis
private async videoThumbnailHandler(request: FastifyRequest<{ Querystring: { url: string; }; }>, reply: FastifyReply) {
const cacheKey = crypto.createHash('md5').update(request.query.url).digest('base64url');
const cacheFile = `videoThumbnail-${cacheKey}.webp`;
if (this.internalStorageService.existsCache(cacheFile)) {
reply.header('Content-Type', 'image/webp');
reply.header('Cache-Control', 'max-age=31536000, immutable');
return reply.sendFile(cacheFile, cacheDir);
}
const file = await this.getStreamAndTypeFromUrl(request.query.url);
if (file === '404') {
reply.code(404);
reply.header('Cache-Control', 'max-age=86400');
return reply.sendFile('/dummy.png', assets);
}
if (file === '204') {
reply.code(204);
reply.header('Cache-Control', 'max-age=86400');
return;
}
if (file.file?.thumbnailUrl) {
return await reply.redirect(301, file.file.thumbnailUrl);
}
if (!file.mime.startsWith('video/')) {
if ('cleanup' in file) {
file.cleanup();
}
reply.code(400);
return;
}
try {
const image = await this.videoProcessingService.generateVideoThumbnail(file.path);
if ('cleanup' in file) {
file.cleanup();
}
this.internalStorageService.saveCacheFromBuffer(cacheFile, image.data);
reply.header('Content-Type', image.type);
reply.header('Cache-Control', 'max-age=31536000, immutable');
return image.data;
} catch (e) {
if ('cleanup' in file) file.cleanup();
throw e;
}
}
@bindThis
private async getStreamAndTypeFromUrl(url: string): Promise<
{ state: 'remote'; fileRole?: 'thumbnail' | 'webpublic' | 'original'; file?: MiDriveFile; mime: string; ext: string | null; path: string; cleanup: () => void; filename: string; }
@ -527,6 +594,9 @@ export class FileServerService {
if (!file.storedInternal) {
if (!(file.isLink && file.uri)) return '204';
const result = await this.downloadAndDetectTypeFromUrl(file.uri);
if (!file.size) {
file.size = (await fs.promises.stat(result.path)).size;
}
return {
...result,
url: file.uri,

View file

@ -5,8 +5,8 @@ block vars
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
- const url = `${config.url}/notes/${note.id}`;
- const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null;
- const images = (note.files || []).filter(file => file.type.startsWith('image/') && !file.isSensitive)
- const videos = (note.files || []).filter(file => file.type.startsWith('video/') && !file.isSensitive)
- const images = note.cw ? [] : (note.files || []).filter(file => file.type.startsWith('image/') && !file.isSensitive)
- const videos = note.cw ? [] : (note.files || []).filter(file => file.type.startsWith('video/') && !file.isSensitive)
block title
= `${title} | ${instanceName}`

View file

@ -392,8 +392,8 @@ importers:
specifier: 1.6.0
version: 1.6.0
tmp:
specifier: 0.2.2
version: 0.2.2
specifier: 0.2.3
version: 0.2.3
tsc-alias:
specifier: 1.8.8
version: 1.8.8
@ -18813,6 +18813,12 @@ packages:
engines: {node: '>=14'}
dependencies:
rimraf: 5.0.5
dev: true
/tmp@0.2.3:
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
engines: {node: '>=14.14'}
dev: false
/tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}