-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
enhance: アバターをサーバーで圧縮して取得する #8337
Changes from all commits
4d4ac5d
bb11a37
ed5805e
0a788d1
8da8329
bb67923
0ed6f4d
323d020
0802306
32dd3bf
1ce9e92
8fcf86c
3236eba
35845bc
0f8f7de
8a32ced
38f84a9
4fdec40
d42c985
af42693
0133902
8a5fdd1
7b4f5ac
2b7ec30
4905299
39f0eb1
6a000b3
599510c
ee6e163
e87e97f
e7d9221
fdb17e0
7102e43
c015001
8979dbc
211bdb0
40dfef9
60d7d03
673361f
50b7ef7
05c011a
3401cf8
8f4c9ad
3f0b914
db6acc9
105e6d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import * as Koa from 'koa'; | ||
import { serverLogger } from '../index'; | ||
import { createTemp } from '@/misc/create-temp'; | ||
import { downloadUrl } from '@/misc/download-url'; | ||
import { detectType } from '@/misc/get-file-info'; | ||
import { StatusError } from '@/misc/fetch'; | ||
import { FILE_TYPE_BROWSERSAFE } from '@/const'; | ||
import * as Acct from 'misskey-js/built/acct'; | ||
import { Users } from '@/models/index'; | ||
import config from '@/config/index'; | ||
import * as sharp from 'sharp'; | ||
import { User } from '@/models/entities/user'; | ||
import * as cors from '@koa/cors'; | ||
import * as Router from '@koa/router'; | ||
|
||
const logger = serverLogger.createSubLogger('proxy-avatar', 'yellow'); | ||
|
||
// Init app | ||
const app = new Koa(); | ||
app.use(cors()); | ||
app.use(async (ctx, next) => { | ||
ctx.set('Content-Security-Policy', `default-src 'none'; img-src 'self'; media-src 'self'; style-src 'unsafe-inline'`); | ||
await next(); | ||
}); | ||
|
||
// Init router | ||
const router = new Router(); | ||
|
||
router.get('(.*)', proxyAvatar); | ||
|
||
// Register router | ||
app.use(router.routes()); | ||
|
||
module.exports = app; | ||
|
||
export async function proxyAvatar(ctx: Koa.Context) { | ||
let user: User | undefined; | ||
|
||
if (ctx.query.acct) { | ||
if (Array.isArray(ctx.query.acct)) { | ||
logger.info(`acct is duplicated`); | ||
return ctx.redirect('/static-assets/user-unknown.png'); | ||
}; | ||
const { username, host } = Acct.parse(ctx.query.acct); | ||
user = await Users.findOne({ | ||
usernameLower: username.toLowerCase(), | ||
host: host === config.host ? null : host, | ||
isSuspended: false, | ||
}); | ||
} else if (ctx.query.userId) { | ||
if (Array.isArray(ctx.query.userId)) { | ||
logger.info(`userId is duplicated`); | ||
return ctx.redirect('/static-assets/user-unknown.png'); | ||
}; | ||
user = await Users.findOne(ctx.query.userId); | ||
} | ||
|
||
if (!user || user.isSuspended) { | ||
logger.info(`user is not found or is suspended: ${ctx.query}`); | ||
return ctx.redirect('/static-assets/user-unknown.png'); | ||
} | ||
if (!user.avatarUrl) return ctx.redirect(Users.getIdenticonUrl(user)); | ||
if (ctx.query.acct || ctx.query.url !== user.avatarUrl) { | ||
// 最新の、キャッシュすべきURLへリダイレクト | ||
logger.info(`redirect`); | ||
const url = new URL(ctx.URL); | ||
url.searchParams.set('url', user.avatarUrl); | ||
if (ctx.query.acct) { | ||
url.searchParams.delete('acct'); | ||
url.searchParams.set('userId', user.id); | ||
} | ||
return ctx.redirect(url.toString()); | ||
} | ||
|
||
// Create temp file | ||
const [path, cleanup] = await createTemp(); | ||
|
||
try { | ||
await downloadUrl(user.avatarUrl, path); | ||
|
||
const { mime, ext } = await detectType(path); | ||
|
||
if (!mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(mime)) { | ||
cleanup(); | ||
return ctx.redirect(Users.getIdenticonUrl(user)); | ||
} | ||
|
||
//#region If image is not animated, redirect to non static url | ||
const metadata = await sharp(path).metadata(); | ||
const isAnimated = metadata.pages && metadata.pages > 1; | ||
if (ctx.query.static && !isAnimated) { | ||
logger.info(`redirect to non static url`); | ||
cleanup(); | ||
const url = new URL(ctx.URL); | ||
url.searchParams.delete('static'); | ||
ctx.status = 301; | ||
ctx.redirect(url.toString()); | ||
return; | ||
} | ||
//#endregion | ||
|
||
ctx.set('Content-Type', 'image/webp'); | ||
ctx.set('Cache-Control', 'max-age=31536000, immutable'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since avatars can change multiple times in the span of a year I think this caching is a bit too agressive. I think something between a week and a month might make more sense? Or remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここに至るまでに、クエリ文字列にurlを持たない場合はリダイレクトされるはずなのですが、どうでしょうか。 |
||
|
||
ctx.body = await sharp(path, { | ||
pages: 'static' in ctx.query ? 256 : 1, | ||
}) | ||
.resize(256, 256, { | ||
fit: 'cover', | ||
withoutEnlargement: true, | ||
}) | ||
.rotate() | ||
.webp({ | ||
quality: 95, | ||
}) | ||
.toBuffer(); | ||
} catch (e) { | ||
serverLogger.error(`${e}`); | ||
|
||
if (e instanceof StatusError && e.isClientError) { | ||
ctx.status = e.statusCode; | ||
} else { | ||
ctx.status = 500; | ||
} | ||
} finally { | ||
cleanup(); | ||
} | ||
} |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see why moving the same code into a separate function is necessary? Since its only one line, at most I think a comment would make sense here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used at
misskey/packages/backend/src/server/proxy/avatar.ts
Line 85 in 105e6d0