Compare commits

...

13 Commits

Author SHA1 Message Date
7a406c1f13 Docker (#2867)
* Dockerize Misskey

* Add a new line at EOF

* Add support Elasticsearch

* /

* Add setup document for docker

* Add english document

* Edit docs

* docker -> Docker

* Arrange format

* Update docker.en.md

* Modify title
2018-10-09 15:09:50 +09:00
9432af2ab5 10.3.0 2018-10-09 15:09:24 +09:00
136b13e7ca Fix bug and refactor 2018-10-09 15:08:31 +09:00
ba1c823fb1 🎨 2018-10-09 14:58:37 +09:00
f1301a4780 fix(package): update @types/redis to version 2.8.7 (#2866) 2018-10-09 09:38:47 +09:00
7957cd4963 fix(package): update @types/node to version 10.11.5 (#2865) 2018-10-09 09:38:40 +09:00
ee6590d03f fix(package): update @types/mongodb to version 3.1.11 (#2864) 2018-10-09 09:38:31 +09:00
f2a1238b20 fix(package): update commander to version 2.19.0 (#2862) 2018-10-09 09:38:21 +09:00
c464183329 Improve theme manager 2018-10-09 06:46:52 +09:00
389f420cad Update src/tools/move-drive-files.ts 2018-10-09 05:46:21 +09:00
6b2888383c 10.2.1 2018-10-09 05:36:39 +09:00
3c38a867b4 Fix bug 2018-10-09 05:35:40 +09:00
7f5a69f4d8 Fix bug 2018-10-09 05:31:26 +09:00
26 changed files with 374 additions and 132 deletions

View File

@ -0,0 +1,13 @@
var user = {
user: 'example-misskey-user',
pwd: 'example-misskey-pass',
roles: [
{
role: 'readWrite',
db: 'misskey'
}
]
};
db.createUser(user);

12
.dockerignore Executable file
View File

@ -0,0 +1,12 @@
.autogen
.git
.github
.travis
.vscode
Dockerfile
build/
docker-compose.yml
node_modules/
mongo/
redis/
elasticsearch/

4
.gitignore vendored
View File

@ -1,5 +1,6 @@
/.config/*
!/.config/example.yml
!/.config/mongo_initdb_example.js
/.vscode
/node_modules
/build
@ -12,3 +13,6 @@ npm-debug.log
run.bat
api-docs.json
*.log
/redis
/mongo
/elasticsearch

28
Dockerfile Normal file
View File

@ -0,0 +1,28 @@
FROM alpine:latest AS base
ENV NODE_ENV=production
RUN apk add --no-cache nodejs nodejs-npm
RUN apk add vips fftw --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
WORKDIR /misskey
COPY . ./
FROM base AS builder
RUN apk add --no-cache gcc g++ python autoconf automake file make nasm
RUN apk add vips-dev fftw-dev --update-cache --repository https://dl-3.alpinelinux.org/alpine/edge/testing/
RUN npm install \
&& npm install -g node-gyp \
&& node-gyp configure \
&& node-gyp build \
&& npm run build
FROM base AS runner
COPY --from=builder /misskey/built ./built
COPY --from=builder /misskey/node_modules ./node_modules
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["npm", "start"]

52
docker-compose.yml Normal file
View File

@ -0,0 +1,52 @@
version: "3"
services:
web:
build: .
restart: always
links:
- mongo
- redis
# - es
ports:
- "127.0.0.1:3000:3000"
networks:
- internal_network
- external_network
redis:
restart: always
image: redis:4.0-alpine
networks:
- internal_network
### Uncomment to enable Redis persistance
# volumes:
# - ./redis:/data
mongo:
restart: always
image: mongo:4.1-bionic
networks:
- internal_network
environment:
MONGO_INITDB_DATABASE: "misskey"
volumes:
- ./.config/mongo_initdb.js:/docker-entrypoint-initdb.d/mongo_initdb.js:ro
### Uncomment to enable MongoDB persistance
# - ./mongo:/data
# es:
# restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# networks:
# - internal_network
#### Uncomment to enable ES persistence
## volumes:
## - ./elasticsearch:/usr/share/elasticsearch/data
networks:
internal_network:
internal: true
external_network:

47
docs/docker.en.md Normal file
View File

@ -0,0 +1,47 @@
Docker Guide
================================================================
This guide describes how to install and setup Misskey with Docker.
[Japanese version also available - 日本語版もあります](./docker.ja.md)
----------------------------------------------------------------
*1.* Make configuration files
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` Copy the `.config/mongo_initdb_example.js` and rename it to `mongo_initdb.js`.
2. Edit `default.yml` and `mongo_initdb.js`.
*2.* Configure Docker
----------------------------------------------------------------
Edit `docker-compose.yml`.
*3.* Build Misskey
----------------------------------------------------------------
Build misskey with the following:
`docker-compose build`
*4.* That is it.
----------------------------------------------------------------
Well done! Now, you have an environment that run to Misskey.
### Launch normally
Just `docker-compose up -d`. GLHF!
### Way to Update to latest version of your Misskey
1. `git fetch`
2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
4. `git stash pop`
5. `docker-compose build`
6. Check [ChangeLog](../CHANGELOG.md) for migration information
7. `docker-compose stop && docker-compose up -d`
### Way to execute cli command:
`docker-compose run --rm web node cli/mark-admin @example`
----------------------------------------------------------------
If you have any questions or troubles, feel free to contact us!

48
docs/docker.ja.md Normal file
View File

@ -0,0 +1,48 @@
Dockerを使ったMisskey構築方法
================================================================
このガイドはDockerを使ったMisskeyセットアップ方法について解説します。
[英語版もあります - English version also available](./docker.en.md)
----------------------------------------------------------------
*1.* 設定ファイルを作成する
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` `.config/example.yml`をコピーし名前を`default.yml`にする
2. `cp .config/mongo_initdb_example.js .config/mongo_initdb.js` `.config/mongo_initdb_example.js`をコピーし名前を`mongo_initdb.js`にする
3. `default.yml``mongo_initdb.js`を編集する
*2.* Dockerの設定
----------------------------------------------------------------
`docker-compose.yml`を編集してください。
*3.* Misskeyのビルド
----------------------------------------------------------------
次のコマンドでMisskeyをビルドしてください:
`docker-compose build`
*4.* 以上です!
----------------------------------------------------------------
お疲れ様でした。これでMisskeyを動かす準備は整いました。
### 通常起動
`docker-compose up -d`するだけです。GLHF!
### Misskeyを最新バージョンにアップデートする方法:
1. `git fetch`
2. `git stash`
3. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
4. `git stash pop`
5. `docker-compose build`
6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
7. `docker-compose stop && docker-compose up -d`
### cliコマンドを実行する方法:
`docker-compose run --rm web node cli/mark-admin @example`
----------------------------------------------------------------
なにかお困りのことがありましたらお気軽にご連絡ください。

View File

@ -307,6 +307,9 @@ common/views/components/theme.vue:
invalid-theme: "テーマが正しくありません。"
already-installed: "既にそのテーマはインストールされています。"
saved: "保存しました"
manage-themes: "テーマの管理"
builtin-themes: "標準テーマ"
my-themes: "マイテーマ"
installed-themes: "インストールされたテーマ"
select-theme: "テーマを選択してください"
uninstall: "アンインストール"

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.2.0",
"clientVersion": "1.0.10362",
"version": "10.3.0",
"clientVersion": "1.0.10375",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@ -58,14 +58,14 @@
"@types/minio": "7.0.0",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.3",
"@types/mongodb": "3.1.10",
"@types/mongodb": "3.1.11",
"@types/ms": "0.7.30",
"@types/node": "10.11.4",
"@types/node": "10.11.5",
"@types/portscanner": "2.1.0",
"@types/pug": "2.0.4",
"@types/qrcode": "1.3.0",
"@types/ratelimiter": "2.1.28",
"@types/redis": "2.8.6",
"@types/redis": "2.8.7",
"@types/request": "2.47.1",
"@types/request-promise-native": "1.0.15",
"@types/rimraf": "2.0.2",
@ -92,7 +92,7 @@
"cafy": "11.3.0",
"chalk": "2.4.1",
"chart.js": "2.7.2",
"commander": "2.17.1",
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "1.0.0",
"dateformat": "3.0.3",
@ -219,6 +219,7 @@
"vue-router": "3.0.1",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.0",
"vue-sweetalert2": "1.5.5",
"vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0",
"vuewordcloud": "18.7.11",

View File

@ -130,3 +130,16 @@ pre
[data-fa]
display inline-block
.swal2-popup
background var(--face) !important
.swal-icon-only
width 180px !important
> .swal2-header
> .swal2-icon
margin 1.25em auto 1.875em
> .swal2-title
display none

View File

@ -91,7 +91,11 @@ export default prop => ({
Vue.set(this.$_ns_target, 'reactionCounts', {});
}
this.$_ns_target.reactionCounts[reaction] = (this.$_ns_target.reactionCounts[reaction] || 0) + 1;
if (this.$_ns_target.reactionCounts[reaction] == null) {
Vue.set(this.$_ns_target.reactionCounts, reaction, 0);
}
this.$_ns_target.reactionCounts[reaction]++;
if (body.userId == this.$store.state.i.id) {
Vue.set(this.$_ns_target, 'myReaction', reaction);

View File

@ -26,92 +26,6 @@ export default class Stream extends EventEmitter {
this.stream.addEventListener('open', this.onOpen);
this.stream.addEventListener('close', this.onClose);
this.stream.addEventListener('message', this.onMessage);
if (user) {
const main = this.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
os.store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
os.store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
os.store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('readAllMessagingMessages', () => {
os.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
os.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
});
main.on('unreadMention', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
os.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
os.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('clientSettingUpdated', x => {
os.store.commit('settings/set', {
key: x.key,
value: x.value
});
});
main.on('homeUpdated', x => {
os.store.commit('settings/setHome', x);
});
main.on('mobileHomeUpdated', x => {
os.store.commit('settings/setMobileHome', x);
});
main.on('widgetUpdated', x => {
os.store.commit('settings/setWidget', {
id: x.id,
data: x.data
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
alert('%i18n:common.my-token-regenerated%');
os.signout();
});
}
}
public useSharedConnection = (channel: string): SharedConnection => {

View File

@ -71,8 +71,7 @@ export default Vue.extend({
this.pingClock = setInterval(() => {
if (this.matching) {
this.connection.send({
type: 'ping',
this.connection.send('ping', {
id: this.matching.id
});
}

View File

@ -103,6 +103,12 @@ export default Vue.extend({
(this as any).api('notes/favorites/create', {
noteId: this.note.id
}).then(() => {
this.$swal({
type: 'success',
showConfirmButton: false,
timer: 1250,
customClass: 'swal-icon-only'
});
this.destroyDom();
});
},

View File

@ -67,22 +67,30 @@
</details>
<details>
<summary>%fa:folder-open% %i18n:@installed-themes%</summary>
<ui-select v-model="selectedInstalledThemeId" placeholder="%i18n:@select-theme%">
<option v-for="x in installedThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
<summary>%fa:folder-open% %i18n:@manage-themes%</summary>
<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%">
<optgroup label="%i18n:@builtin-themes%">
<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup label="%i18n:@my-themes%">
<option v-for="x in installedThemes.filter(t => t.author == this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
<optgroup label="%i18n:@installed-themes%">
<option v-for="x in installedThemes.filter(t => t.author != this.$store.state.i.username)" :value="x.id" :key="x.id">{{ x.name }}</option>
</optgroup>
</ui-select>
<template v-if="selectedInstalledTheme">
<ui-input readonly :value="selectedInstalledTheme.author">
<template v-if="selectedTheme">
<ui-input readonly :value="selectedTheme.author">
<span>%i18n:@author%</span>
</ui-input>
<ui-textarea v-if="selectedInstalledTheme.desc" readonly :value="selectedInstalledTheme.desc">
<ui-textarea v-if="selectedTheme.desc" readonly :value="selectedTheme.desc">
<span>%i18n:@desc%</span>
</ui-textarea>
<ui-textarea readonly :value="selectedInstalledThemeCode">
<ui-textarea readonly :value="selectedThemeCode">
<span>%i18n:@theme-code%</span>
</ui-textarea>
<ui-button @click="export_()" link :download="`${selectedInstalledTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
<ui-button @click="uninstall()">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
</template>
</details>
</div>
@ -117,8 +125,9 @@ export default Vue.extend({
data() {
return {
builtinThemes: builtinThemes,
installThemeCode: null,
selectedInstalledThemeId: null,
selectedThemeId: null,
myThemeBase: 'light',
myThemeName: '',
myThemeDesc: '',
@ -155,14 +164,14 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'darkTheme', value }); }
},
selectedInstalledTheme() {
if (this.selectedInstalledThemeId == null) return null;
return this.installedThemes.find(x => x.id == this.selectedInstalledThemeId);
selectedTheme() {
if (this.selectedThemeId == null) return null;
return this.themes.find(x => x.id == this.selectedThemeId);
},
selectedInstalledThemeCode() {
if (this.selectedInstalledTheme == null) return null;
return JSON5.stringify(this.selectedInstalledTheme, null, '\t');
selectedThemeCode() {
if (this.selectedTheme == null) return null;
return JSON5.stringify(this.selectedTheme, null, '\t');
},
myTheme(): any {
@ -238,7 +247,7 @@ export default Vue.extend({
},
uninstall() {
const theme = this.selectedInstalledTheme;
const theme = this.selectedTheme;
const themes = this.$store.state.device.themes.filter(t => t.id != theme.id);
this.$store.commit('device/set', {
key: 'themes', value: themes
@ -251,7 +260,7 @@ export default Vue.extend({
}
export_() {
const blob = new Blob([this.selectedInstalledThemeCode], {
const blob = new Blob([this.selectedThemeCode], {
type: 'application/json5'
});
this.$refs.export.$el.href = window.URL.createObjectURL(blob);

View File

@ -113,8 +113,7 @@ export default define({
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send({
type: 'requestLog',
this.connection.send('requestLog',{
id: Math.random().toString()
});
},

View File

@ -91,8 +91,7 @@ export default Vue.extend({
mounted() {
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send({
type: 'requestLog',
this.connection.send('requestLog', {
id: Math.random().toString()
});
},

View File

@ -181,8 +181,7 @@ export default Vue.extend({
onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({
type: 'readNotification',
(this as any).os.stream.send('readNotification', {
id: notification.id
});

View File

@ -77,8 +77,7 @@ export default Vue.extend({
mounted() {
this.connection.on('stats', this.onStats);
this.connection.on('statsLog', this.onStatsLog);
this.connection.send({
type: 'requestLog',
this.connection.send('requestLog', {
id: Math.random().toString(),
length: 200
});

View File

@ -113,8 +113,7 @@ export default Vue.extend({
onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({
type: 'readNotification',
(this as any).os.stream.send('readNotification', {
id: notification.id
});

View File

@ -8,6 +8,7 @@ import VueRouter from 'vue-router';
import * as TreeView from 'vue-json-tree-view';
import VAnimateCss from 'v-animate-css';
import VModal from 'vue-js-modal';
import VueSweetalert2 from 'vue-sweetalert2';
import VueHotkey from './common/hotkey';
import App from './app.vue';
@ -26,6 +27,7 @@ Vue.use(TreeView);
Vue.use(VAnimateCss);
Vue.use(VModal);
Vue.use(VueHotkey);
Vue.use(VueSweetalert2);
// Register global directives
require('./common/views/directives');

View File

@ -212,7 +212,7 @@ export default class MiOS extends EventEmitter {
const fetched = () => {
this.emit('signedin');
this.stream = new Stream(this);
this.initStream();
// Finish init
callback();
@ -247,12 +247,103 @@ export default class MiOS extends EventEmitter {
// Finish init
callback();
this.stream = new Stream(this);
this.initStream();
}
});
}
}
@autobind
private initStream() {
this.stream = new Stream(this);
if (this.store.getters.isSignedIn) {
const main = this.stream.useSharedConnection('main');
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
this.store.dispatch('mergeMe', i);
});
main.on('readAllNotifications', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: false
});
});
main.on('unreadNotification', () => {
this.store.dispatch('mergeMe', {
hasUnreadNotification: true
});
});
main.on('readAllMessagingMessages', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: false
});
});
main.on('unreadMessagingMessage', () => {
this.store.dispatch('mergeMe', {
hasUnreadMessagingMessage: true
});
});
main.on('unreadMention', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: true
});
});
main.on('readAllUnreadMentions', () => {
this.store.dispatch('mergeMe', {
hasUnreadMentions: false
});
});
main.on('unreadSpecifiedNote', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: true
});
});
main.on('readAllUnreadSpecifiedNotes', () => {
this.store.dispatch('mergeMe', {
hasUnreadSpecifiedNotes: false
});
});
main.on('clientSettingUpdated', x => {
this.store.commit('settings/set', {
key: x.key,
value: x.value
});
});
main.on('homeUpdated', x => {
this.store.commit('settings/setHome', x);
});
main.on('mobileHomeUpdated', x => {
this.store.commit('settings/setMobileHome', x);
});
main.on('widgetUpdated', x => {
this.store.commit('settings/setWidget', {
id: x.id,
data: x.data
});
});
// トークンが再生成されたとき
// このままではMisskeyが利用できないので強制的にサインアウトさせる
main.on('myTokenRegenerated', () => {
alert('%i18n:common.my-token-regenerated%');
this.signout();
});
}
}
/**
* Register service worker
*/

View File

@ -98,8 +98,7 @@ export default Vue.extend({
onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({
type: 'readNotification',
(this as any).os.stream.send('readNotification', {
id: notification.id
});

View File

@ -58,8 +58,7 @@ export default Vue.extend({
methods: {
onNotification(notification) {
// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
this.connection.send({
type: 'readNotification',
(this as any).os.stream.send('readNotification', {
id: notification.id
});

View File

@ -17,7 +17,7 @@ const summarize = (note: any): string => {
summary += note.text ? note.text : '';
// ファイルが添付されているとき
if (note.files.length != 0) {
if ((note.files || []).length != 0) {
summary += ` (${note.files.length}つのファイル)`;
}

View File

@ -1,5 +1,6 @@
import * as Minio from 'minio';
import * as uuid from 'uuid';
const sequential = require('promise-sequential');
import DriveFile, { DriveFileChunk, getDriveFileBucket } from '../models/drive-file';
import DriveFileThumbnail, { DriveFileThumbnailChunk } from '../models/drive-file-thumbnail';
import config from '../config';
@ -11,7 +12,7 @@ DriveFile.find({
withoutChunks: false
}]
}).then(async files => {
files.forEach(async file => {
await sequential(files.map(file => async () => {
const minio = new Minio.Client(config.drive.config);
const keyDir = `${config.drive.prefix}/${uuid.v4()}`;
@ -60,5 +61,7 @@ DriveFile.find({
await DriveFileThumbnail.remove({ _id: thumbnail._id });
}
//#endregion
});
console.log('done', file._id);
}));
});