Compare commits

...

27 Commits
5.1.0 ... 5.3.0

Author SHA1 Message Date
1c25dbed66 5.3.0 2018-07-27 03:23:17 +09:00
7e8c5c0c3c Improve readability 2018-07-27 03:21:50 +09:00
0b747b901c Merge pull request #1998 from syuilo/greenkeeper/typescript-eslint-parser-17.0.0
Update typescript-eslint-parser to the latest version 🚀
2018-07-27 01:54:09 +09:00
8dd5051201 Merge pull request #1990 from mei23/mei-osurl
オブジェクトストレージの参照URLを上書きできるようにする
2018-07-27 01:53:42 +09:00
f7b0fedc9d Merge pull request #1989 from mei23/mei-oscc
オブジェクトストレージ格納時にCache-Controlを指定する
2018-07-27 01:52:19 +09:00
0411d0b242 fix(package): update typescript-eslint-parser to version 17.0.0 2018-07-26 13:37:52 +00:00
3fcc793269 Fix problem displaying button in profile page 2018-07-26 21:47:02 +09:00
fd27a0efef Hide follow button of my account 2018-07-26 21:45:43 +09:00
4474a2568e Change VisibilityButton's icon when changing visibility 2018-07-26 21:44:21 +09:00
9d944243a3 Add S3 examples 2018-07-26 17:42:08 +09:00
8ef38ebab1 Add config.drive.baseUrl 2018-07-26 17:29:05 +09:00
f457a23eab Merge pull request #1988 from syuilo/greenkeeper/element-ui-2.4.5
Update element-ui to the latest version 🚀
2018-07-26 17:16:45 +09:00
5d1eeaf1d8 fix(package): update element-ui to version 2.4.5 2018-07-26 08:16:21 +00:00
77f732c6a4 5.2.1 2018-07-26 17:15:20 +09:00
ac07f04ad8 Update job queue setting 2018-07-26 17:15:00 +09:00
dddd760efd Fix bug 2018-07-26 17:13:55 +09:00
0f7fbacb17 Fix bug 2018-07-26 17:10:43 +09:00
2697107770 5.2.0 2018-07-26 17:04:33 +09:00
e1e1cd0574 Update job queue settings 2018-07-26 17:02:34 +09:00
93786aa510 Merge branch 'master' of https://github.com/syuilo/misskey 2018-07-26 16:51:00 +09:00
d8b9a8715b ✌️ 2018-07-26 16:50:50 +09:00
e8783b15b1 Set Cache-Control to object-storage 2018-07-26 16:06:43 +09:00
0995d5c5a2 Merge pull request #1986 from acid-chicken/master
Resolves #1985
2018-07-26 13:49:24 +09:00
0852045928 Update misskey-flavored-markdown.ts 2018-07-26 13:48:08 +09:00
04de0e9a50 Update hashtags.vue 2018-07-26 13:47:06 +09:00
951b693d17 Merge pull request #1983 from mei23/mei-osct 2018-07-26 11:56:00 +09:00
308f357c4f Set Content-Type to object-storage 2018-07-26 10:47:12 +09:00
12 changed files with 102 additions and 52 deletions

View File

@ -68,6 +68,29 @@ drive:
# accessKey: # accessKey:
# secretKey: # secretKey:
# S3 example
# storage: 'minio'
# bucket: bucket-name
# prefix: files
# config:
# endPoint: s3-us-west-2.amazonaws.com
# region: us-west-2
# secure: true
# accessKey: XXX
# secretKey: YYY
# S3 example (with CDN, custom domain)
# storage: 'minio'
# bucket: drive.example.com
# prefix: files
# baseUrl: https://drive.example.com
# config:
# endPoint: s3-us-west-2.amazonaws.com
# region: us-west-2
# secure: true
# accessKey: XXX
# secretKey: YYY
# #
# Below settings are optional # Below settings are optional
# #

View File

@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "5.1.0", "version": "5.3.0",
"clientVersion": "1.0.7561", "clientVersion": "1.0.7588",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,
@ -98,7 +98,7 @@
"diskusage": "0.2.4", "diskusage": "0.2.4",
"dompurify": "1.0.5", "dompurify": "1.0.5",
"elasticsearch": "15.1.1", "elasticsearch": "15.1.1",
"element-ui": "2.4.4", "element-ui": "2.4.5",
"emojilib": "2.3.0", "emojilib": "2.3.0",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eslint": "5.0.1", "eslint": "5.0.1",
@ -194,7 +194,7 @@
"ts-node": "7.0.0", "ts-node": "7.0.0",
"tslint": "5.10.0", "tslint": "5.10.0",
"typescript": "2.9.2", "typescript": "2.9.2",
"typescript-eslint-parser": "16.0.1", "typescript-eslint-parser": "17.0.0",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"url-loader": "1.0.1", "url-loader": "1.0.1",
"uuid": "3.3.2", "uuid": "3.3.2",

View File

@ -92,7 +92,7 @@ export default Vue.component('misskey-flavored-markdown', {
case 'hashtag': case 'hashtag':
return createElement('a', { return createElement('a', {
attrs: { attrs: {
href: `${url}/tags/${token.hashtag}`, href: `${url}/tags/${encodeURIComponent(token.hashtag)}`,
target: '_blank' target: '_blank'
} }
}, token.content); }, token.content);

View File

@ -11,7 +11,7 @@
<div> <div>
<div v-for="stat in stats" :key="stat.tag"> <div v-for="stat in stats" :key="stat.tag">
<div class="tag"> <div class="tag">
<router-link :to="`/tags/${ stat.tag }`" :title="stat.tag">#{{ stat.tag }}</router-link> <router-link :to="`/tags/${ encodeURIComponent(stat.tag) }`" :title="stat.tag">#{{ stat.tag }}</router-link>
<p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p> <p>{{ '%i18n:@count%'.replace('{}', stat.usersCount) }}</p>
</div> </div>
<x-chart class="chart" :src="stat.chart"/> <x-chart class="chart" :src="stat.chart"/>

View File

@ -38,7 +38,13 @@
<button class="poll" title="%i18n:@create-poll%" @click="poll = true">%fa:chart-pie%</button> <button class="poll" title="%i18n:@create-poll%" @click="poll = true">%fa:chart-pie%</button>
<button class="poll" title="内容を隠す" @click="useCw = !useCw">%fa:eye-slash%</button> <button class="poll" title="内容を隠す" @click="useCw = !useCw">%fa:eye-slash%</button>
<button class="geo" title="位置情報を添付する" @click="geo ? removeGeo() : setGeo()">%fa:map-marker-alt%</button> <button class="geo" title="位置情報を添付する" @click="geo ? removeGeo() : setGeo()">%fa:map-marker-alt%</button>
<button class="visibility" title="公開範囲" @click="setVisibility" ref="visibilityButton">%fa:lock%</button> <button class="visibility" title="公開範囲" @click="setVisibility" ref="visibilityButton">
<span v-if="visibility === 'public'">%fa:globe%</span>
<span v-if="visibility === 'home'">%fa:home%</span>
<span v-if="visibility === 'followers'">%fa:unlock%</span>
<span v-if="visibility === 'specified'">%fa:envelope%</span>
<span v-if="visibility === 'private'">%fa:lock%</span>
</button>
<p class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</p> <p class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</p>
<button :class="{ posting }" class="submit" :disabled="!canPost" @click="post"> <button :class="{ posting }" class="submit" :disabled="!canPost" @click="post">
{{ posting ? '%i18n:@posting%' : submitText }}<mk-ellipsis v-if="posting"/> {{ posting ? '%i18n:@posting%' : submitText }}<mk-ellipsis v-if="posting"/>

View File

@ -19,7 +19,7 @@
<p>%i18n:@followers%</p><a>{{ u.followersCount }}</a> <p>%i18n:@followers%</p><a>{{ u.followersCount }}</a>
</div> </div>
</div> </div>
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="u"/> <mk-follow-button v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u"/>
</template> </template>
</div> </div>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="profile"> <div class="profile" v-if="$store.getters.isSignedIn">
<div class="friend-form" v-if="$store.getters.isSignedIn && $store.state.i.id != user.id"> <div class="friend-form" v-if="$store.state.i.id != user.id">
<mk-follow-button :user="user" size="big"/> <mk-follow-button :user="user" size="big"/>
<p class="followed" v-if="user.isFollowed">%i18n:@follows-you%</p> <p class="followed" v-if="user.isFollowed">%i18n:@follows-you%</p>
<p class="stalk" v-if="user.isFollowing"> <p class="stalk" v-if="user.isFollowing">
@ -9,7 +9,7 @@
</p> </p>
</div> </div>
<div class="action-form"> <div class="action-form">
<button class="mute ui" @click="user.isMuted ? unmute() : mute()"> <button class="mute ui" @click="user.isMuted ? unmute() : mute()" v-if="$store.state.i.id != user.id">
<span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span> <span v-if="user.isMuted">%fa:eye% %i18n:@unmute%</span>
<span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span> <span v-if="!user.isMuted">%fa:eye-slash% %i18n:@mute%</span>
</button> </button>

View File

@ -53,6 +53,7 @@ export type Source = {
storage: string; storage: string;
bucket?: string; bucket?: string;
prefix?: string; prefix?: string;
baseUrl?: string;
config?: any; config?: any;
}; };

View File

@ -12,13 +12,16 @@ const queue = new Queue('misskey', {
}, },
removeOnSuccess: true, removeOnSuccess: true,
removeOnFailure: true removeOnFailure: true,
getEvents: false,
sendEvents: false,
storeJobs: false
}); });
export function createHttpJob(data: any) { export function createHttpJob(data: any) {
return queue.createJob(data) return queue.createJob(data)
.retries(4) //.retries(4)
.backoff('exponential', 16384) // 16s //.backoff('exponential', 16384) // 16s
.save(); .save();
} }
@ -32,5 +35,5 @@ export function deliver(user: ILocalUser, content: any, to: any) {
} }
export default function() { export default function() {
queue.process(8, http); queue.process(128, http);
} }

View File

@ -14,6 +14,34 @@ import htmlToMFM from '../../../mfm/html-to-mfm';
const log = debug('misskey:activitypub'); const log = debug('misskey:activitypub');
function validatePerson(x: any) {
if (x == null) {
return new Error('invalid person: object is null');
}
if (x.type != 'Person' && x.type != 'Service') {
return new Error(`invalid person: object is not a person or service '${x.type}'`);
}
if (typeof x.preferredUsername !== 'string') {
return new Error('invalid person: preferredUsername is not a string');
}
if (typeof x.inbox !== 'string') {
return new Error('invalid person: inbox is not a string');
}
if (!validateUsername(x.preferredUsername)) {
return new Error('invalid person: invalid username');
}
if (!isValidName(x.name == '' ? null : x.name)) {
return new Error('invalid person: invalid name');
}
return null;
}
/** /**
* Personをフェッチします。 * Personをフェッチします。
* *
@ -47,28 +75,10 @@ export async function createPerson(value: any, resolver?: Resolver): Promise<IUs
const object = await resolver.resolve(value) as any; const object = await resolver.resolve(value) as any;
if (object == null) { const err = validatePerson(object);
throw new Error('invalid person: object is null');
}
if (object.type != 'Person' && object.type != 'Service') { if (err) {
throw new Error(`invalid person: object is not a person or service '${object.type}'`); throw err;
}
if (typeof object.preferredUsername !== 'string') {
throw new Error('invalid person: preferredUsername is not a string');
}
if (typeof object.inbox !== 'string') {
throw new Error('invalid person: inbox is not a string');
}
if (!validateUsername(object.preferredUsername)) {
throw new Error('invalid person: invalid username');
}
if (!isValidName(object.name == '' ? null : object.name)) {
throw new Error('invalid person: invalid name');
} }
const person: IPerson = object; const person: IPerson = object;
@ -198,12 +208,10 @@ export async function updatePerson(value: string | IObject, resolver?: Resolver)
const object = await resolver.resolve(value) as any; const object = await resolver.resolve(value) as any;
if ( const err = validatePerson(object);
object == null ||
object.type !== 'Person' if (err) {
) { throw err;
log(`invalid person: ${JSON.stringify(object, null, 2)}`);
throw new Error('invalid person');
} }
const person: IPerson = object; const person: IPerson = object;

View File

@ -52,7 +52,7 @@ export default (endpoint: string, user: IUser, app: IApp, data: any, file?: any)
const time = after - before; const time = after - before;
if (time > 500) { if (time > 1000) {
console.warn(`SLOW API CALL DETECTED: ${ep.name} (${ time }ms)`); console.warn(`SLOW API CALL DETECTED: ${ep.name} (${ time }ms)`);
} }
} catch (e) { } catch (e) {

View File

@ -25,7 +25,14 @@ async function save(readable: stream.Readable, name: string, type: string, hash:
const minio = new Minio.Client(config.drive.config); const minio = new Minio.Client(config.drive.config);
const id = uuid.v4(); const id = uuid.v4();
const obj = `${config.drive.prefix}/${id}`; const obj = `${config.drive.prefix}/${id}`;
await minio.putObject(config.drive.bucket, obj, readable);
const baseUrl = config.drive.baseUrl
|| `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }`;
await minio.putObject(config.drive.bucket, obj, readable, size, {
'Content-Type': type,
'Cache-Control': 'max-age=31536000, immutable'
});
Object.assign(metadata, { Object.assign(metadata, {
withoutChunks: true, withoutChunks: true,
@ -33,7 +40,7 @@ async function save(readable: stream.Readable, name: string, type: string, hash:
storageProps: { storageProps: {
id: id id: id
}, },
url: `${ config.drive.config.secure ? 'https' : 'http' }://${ config.drive.config.endPoint }${ config.drive.config.port ? ':' + config.drive.config.port : '' }/${ config.drive.bucket }/${ obj }` url: `${ baseUrl }/${ obj }`
}); });
const file = await DriveFile.insert({ const file = await DriveFile.insert({
@ -242,6 +249,7 @@ export default async function(
const calcAvg = async () => { const calcAvg = async () => {
log('calculate average color...'); log('calculate average color...');
try {
const info = await (img as any).stats(); const info = await (img as any).stats();
const r = Math.round(info.channels[0].mean); const r = Math.round(info.channels[0].mean);
@ -253,6 +261,7 @@ export default async function(
const value = info.isOpaque ? [r, g, b] : [r, g, b, 255]; const value = info.isOpaque ? [r, g, b] : [r, g, b, 255];
properties['avgColor'] = value; properties['avgColor'] = value;
} catch (e) { }
}; };
propPromises = [calcWh(), calcAvg()]; propPromises = [calcWh(), calcAvg()];