Compare commits

..

67 Commits

Author SHA1 Message Date
9c34f96cdd feat: Add convert video container button lang 2022-03-13 16:14:57 +09:00
74a80b3405 feat: Add convert video container button 2022-03-13 16:11:08 +09:00
84da9d4cd4 fix: wip 2022-03-13 14:37:55 +09:00
b48f76aad1 fix: update package.json 2022-03-13 14:14:44 +09:00
78b9550eca fix: debug 2022-03-13 14:09:44 +09:00
90c89c3083 fix: debug 2022-03-12 02:24:23 +09:00
c63d7dc898 fix: wippppppppppppppppppppppaaafaafawfwafwaafw 2022-03-12 02:19:56 +09:00
74e695a320 fix: wipppppppppppppppppppppp 2022-03-12 02:11:15 +09:00
f269ceaee9 fix: wip 2022-03-12 01:30:08 +09:00
57a106a192 fix: wip 2022-03-12 01:23:49 +09:00
5563d6bdeb fix: wip 2022-03-11 22:48:50 +09:00
ab59a5b659 fix: dont export select-file-enc.ts 2022-03-11 22:45:03 +09:00
a81f8b8275 feat: Add video convert button 2022-03-11 22:34:38 +09:00
c99c4b73f4 Create select-file-enc.ts 2022-03-11 22:05:08 +09:00
995d8f2f5d Update post-form.vue 2022-03-11 22:03:36 +09:00
a4a0d33348 Merge tag '12.108.0-simkey' into develop 2022-03-09 23:09:14 +09:00
7d1f3cd1e8 Merge branch 'develop' of github.com:sim1222/misskey into develop 2022-02-21 19:08:08 +09:00
dc13627a68 add gitignore 2022-02-21 19:08:04 +09:00
7024f6651f Merge remote-tracking branch 'origin/sim-dev-reload-button-2' into develop 2022-02-17 01:19:30 +09:00
4f9aca0aa3 add reload button 2022-02-17 01:18:37 +09:00
d6b15add1b Merge remote-tracking branch 'misskey-dev/master' into develop 2022-02-12 17:39:23 +09:00
9b7be06eef Merge remote-tracking branch 'misskey-dev/develop' into develop 2022-02-11 22:20:04 +09:00
73d66fc439 Merge tag '12.106.0-simkey' into develop 2022-02-11 19:24:42 +09:00
8e173a6d8e Merge tag '12.105.0' into develop 2022-02-09 22:00:47 +09:00
aee5193ef6 Merge branch 'master' into develop 2022-02-09 17:16:54 +09:00
2892a31142 Merge remote-tracking branch 'origin/develop' into develop 2022-02-09 17:14:50 +09:00
90bb0cec37 fix: Nya Translation from ja-JP 2022-02-09 17:14:30 +09:00
19775507fc Merge pull request #26 from sim1222/develop
Merge Develop 12.104.0
2022-02-09 16:40:32 +09:00
9d4f621833 fix conflct 2022-02-09 16:39:15 +09:00
2e095b4863 Merge pull request #24 from sim1222/develop
Merge Develop 12.103.1
2022-02-03 10:48:14 +09:00
5d9b9eeaad Merge branch 'master' into develop 2022-02-03 10:48:04 +09:00
98f5cb6cd2 git push origin developMerge branch 'misskey-dev-develop' into develop 2022-02-03 01:43:09 +00:00
b88c4d6d34 fix comf 2022-02-03 01:42:58 +00:00
6238ba965b wip add button 2022-01-30 19:42:54 +09:00
8489c3ca7b Merge pull request #22 from sim1222/v12.102.1
Merge develop V12.102.1
2022-01-29 05:34:12 +09:00
33871f3cb8 Merge branch 'master' into v12.102.1 2022-01-29 05:34:03 +09:00
5eb758fa28 update package.json 2022-01-29 04:17:15 +09:00
56d8f7f257 Merge pull request #19 from sim1222/develop
Merge Develop v12.102.0
2022-01-27 00:53:29 +09:00
74c3e6d483 Merge branch 'misskey-dev-develop' into develop 2022-01-27 00:52:19 +09:00
ead62a538f fix confrict 2022-01-27 00:52:09 +09:00
72058b30c2 Merge branch 'misskey-dev:develop' into develop 2022-01-26 20:09:42 +09:00
22102639a8 Merge pull request #16 from sim1222/develop
Change Color
2022-01-13 03:11:14 +09:00
ee4289076f Change Color 2022-01-13 03:10:42 +09:00
be011d3985 Merge pull request #15 from sim1222/develop
change color
2022-01-09 21:43:44 +09:00
6ef86df368 change color 2022-01-09 21:43:12 +09:00
34e099984f Merge pull request #14 from sim1222/develop
Fix ja-NY Language
2022-01-04 19:50:23 +09:00
5a1c6be944 Fix ja-NY Language 2022-01-04 19:33:41 +09:00
25991cfdc1 Merge pull request #11 from sim1222/develop
Merge Develop 12.101.1
2021-12-29 18:15:50 +09:00
b1a526aeea fix conflict 2021-12-29 18:15:03 +09:00
f31ef70ce7 Merge pull request #9 from sim1222/develop
Merge develop 12.101.0
2021-12-29 15:12:02 +09:00
09a751b992 git push origin developMerge branch 'misskey-dev-develop' into develop 2021-12-29 15:08:42 +09:00
a41e4ed4fd fix conflict 2021-12-29 15:08:31 +09:00
b77f7f5041 Merge pull request #7 from sim1222/develop
change repo url
2021-12-28 16:16:19 +09:00
7adcf99865 change repo url 2021-12-28 16:15:51 +09:00
cc2d5480f3 Merge pull request #6 from sim1222/develop
wip
2021-12-28 03:16:18 +09:00
fdd129fb83 wip 2021-12-28 03:15:56 +09:00
8dd3fef0e2 Merge pull request #5 from sim1222/develop
fix wip
2021-12-28 03:04:25 +09:00
f1c66f09e1 fix wip 2021-12-28 03:03:58 +09:00
6f328f2ccf Merge pull request #4 from sim1222/develop
fix typo
2021-12-28 02:55:19 +09:00
70b3d598c6 fix typo 2021-12-28 02:54:53 +09:00
668c403ece Merge pull request #3 from sim1222/develop
fix NYA JP
2021-12-28 02:49:24 +09:00
c873f17080 fix NYA JP 2021-12-28 02:48:55 +09:00
3f6691bd5e Merge pull request #2 from sim1222/develop
vsddgvsgdsgsdsg
2021-12-28 02:27:13 +09:00
260ece9881 Update package.json 2021-12-28 02:26:10 +09:00
9a4c04fe79 Merge pull request #1 from sim1222/sim-dev-ja-nya
Sim dev ja nya
2021-12-28 02:25:13 +09:00
24896d4a36 Merge branch 'misskey-dev:develop' into sim-dev-ja-nya 2021-12-28 02:17:22 +09:00
8aea52e9b4 てぬきにゃ翻訳 2021-12-28 02:05:40 +09:00
575 changed files with 12068 additions and 6543 deletions

View File

@ -1,18 +0,0 @@
name: Docker Image CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build the Docker image
run: docker build . --file Dockerfile --tag my-image-name:$(date +%s)

View File

@ -14,12 +14,12 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-node@v3
- uses: actions/setup-node@v1
with:
node-version: 16.x
cache: 'yarn'
cache-dependency-path: |
packages/backend/yarn.lock
packages/client/yarn.lock
node-version: 12.x
- uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
- run: yarn install
- run: yarn lint

View File

@ -33,13 +33,9 @@ jobs:
with:
submodules: true
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
cache-dependency-path: |
packages/backend/yarn.lock
packages/client/yarn.lock
- name: Install dependencies
run: yarn install
- name: Check yarn.lock
@ -84,13 +80,13 @@ jobs:
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
cache-dependency-path: |
packages/backend/yarn.lock
packages/client/yarn.lock
- uses: actions/cache@v2
with:
path: '**/node_modules'
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
- name: Install dependencies
run: yarn install
- name: Check yarn.lock

2
.gitignore vendored
View File

@ -42,3 +42,5 @@ ormconfig.json
*.blend3
*.blend4
*.blend5
start.sh

View File

@ -10,62 +10,6 @@
You should also include the user name that made the change.
-->
## 12.110.1 (2022/04/23)
### Bugfixes
- Fix GOP rendering @syuilo
- Improve performance of antenna, clip, and list @xianon
## 12.110.0 (2022/04/11)
### Improvements
- Improve webhook @syuilo
- Client: Show loading icon on splash screen @syuilo
### Bugfixes
- API: parameter validation of users/show was wrong
- Federation: リモートインスタンスへのダイレクト投稿が届かない問題を修正 @syuilo
## 12.109.2 (2022/04/03)
### Bugfixes
- API: admin/update-meta was not working @syuilo
- Client: テーマを切り替えたり読み込んだりするとmeta[name="theme-color"]のcontentがundefinedになる問題を修正 @tamaina
## 12.109.1 (2022/04/02)
### Bugfixes
- API: Renoteが行えない問題を修正
## 12.109.0 (2022/04/02)
### Improvements
- Webhooks @syuilo
- Bull Dashboardを組み込み、ジョブキューの確認や操作を行えるように @syuilo
- Bull Dashboardを開くには、最初だけ一旦ログアウトしてから再度管理者権限を持つアカウントでログインする必要があります
- Check that installed Node.js version fulfills version requirement @ThatOneCalculator
- Server: overall performance improvements @syuilo
- Federation: avoid duplicate activity delivery @Johann150
- Federation: limit federation of reactions on direct notes @Johann150
- Client: タッチパッド・タッチスクリーンでのデッキの操作性を向上 @tamaina
### Bugfixes
- email address validation was not working @ybw2016v
- API: fix endpoint endpoint @Johann150
- API: fix admin/meta endpoint @syuilo
- API: improved validation and documentation for endpoints that accept different variants of input @Johann150
- API: `notes/create`: The `mediaIds` property is now deprecated. @Johann150
- Use `fileIds` instead, it has the same behaviour.
- Client: URIエンコーディングが異常でdecodeURIComponentが失敗するとURLが表示できなくなる問題を修正 @tamaina
## 12.108.1 (2022/03/12)
### Bugfixes
- リレーが動作しない問題を修正 @xianonn
- ulidを使用していると動作しない問題を修正 @syuilo
- 外部からOGPが正しく取得できない問題を修正 @syuilo
- instance can not get the files from other instance when there are items in allowedPrivateNetworks in .config/default.yml @ybw2016v
## 12.108.0 (2022/03/09)
### NOTE

View File

@ -6,9 +6,6 @@ Also, you might receive comments on your Issue/PR in Japanese, but you do not ne
The accuracy of machine translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language.
It will also allow the reader to use the translation tool of their preference if necessary.
## Roadmap
See [ROADMAP.md](./ROADMAP.md)
## Issues
Before creating an issue, please check the following:
- To avoid duplication, please search for similar issues before creating a new issue.
@ -201,13 +198,11 @@ MongoDBの時とは違い、findOneでレコードを取得する時に対象レ
MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`とか書くとバグる。代わりに`if (x == null)`と書いてください
### Migration作成方法
packages/backendで:
```sh
npx typeorm migration:generate -d ormconfig.js -o <migration name>
```
npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前 -o
```
- 生成後、ファイルをmigration下に移してください
- 作成されたスクリプトは不必要な変更を含むため除去してください
作成されたスクリプトは不必要な変更を含むため除去してください
### コネクションには`markRaw`せよ
**Vueのコンポーネントのdataオプションとして**misskey.jsのコネクションを設定するとき、必ず`markRaw`でラップしてください。インスタンスが不必要にリアクティブ化されることで、misskey.js内の処理で不具合が発生するとともに、パフォーマンス上の問題にも繋がる。なお、Composition APIを使う場合はこの限りではない(リアクティブ化はマニュアルなため)。

View File

@ -32,3 +32,4 @@ COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node
COPY . ./
CMD ["npm", "run", "migrateandstart"]

173
README.md
View File

@ -1,7 +1,168 @@
## これってなに?
[Misskey](https://github.com/misskey-dev/misskey)ベースのSNS
[![Misskey](https://github.com/misskey-dev/assets/blob/main/banner.png?raw=true)](https://join.misskey.page/)
## 本家との違い
[ここ](https://nullc.at/nca10.net/)にかいてあるよ
## 導入方法
[MisskeyHub](https://misskey-hub.net/docs/install.html)をみてね
<div align="center">
**🌎 A forever evolving, interplanetary microblogging platform. 🚀**
**Misskey** is a distributed microblogging platform with advanced features such as Reactions and a highly customizable UI.
[Learn more](https://misskey-hub.net/)
---
[✨ Find an instance](https://misskey-hub.net/instances.html)
[📦 Create your own instance](https://misskey-hub.net/docs/install.html)
[🛠️ Contribute](./CONTRIBUTING.md)
[🚀 Join the community](https://discord.gg/Wp8gVStHW3)
---
<a href="https://www.patreon.com/syuilo"><img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" alt="Become a Patron!" width="160" /></a>
</div>
<div>
<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai.png?raw=true" align="right" height="320px"/></a>
## ✨ Features
- **ActivityPub support**\
It is possible to interact with other software.
- **Reactions**\
You can add "reactions" to each post, making it easy for you to express your feelings.
- **Drive**\
An interface to manage uploaded files such as images, videos, sounds, etc.
You can also organize your favorite content into folders, making it easy to share again.
- **Rich Web UI**\
Misskey has a rich WebUI by default.
It is highly customizable by flexibly changing the layout and installing various widgets and themes.
Furthermore, plug-ins can be created using AiScript, a original programming language.
- and more...
</div>
<div style="clear: both;"></div>
## Sponsors
<div align="center">
<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a>
</div>
## Backers
<!-- PATREON_START -->
<table><tr>
<td><img src="https://c8.patreon.com/2/200/20832595" alt="Roujo " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/27956229" alt="Oliver Maximilian Seidel" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/27648259" alt="みなしま " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24430516/b1964ac5b9f746d2a12ff53dbc9aa40a/1.jpg?token-time=2145916800&token-hash=bmEiMGYpp3bS7hCCbymjGGsHBZM3AXuBOFO3Kro37PU%3D" alt="Eduardo Quiros" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=20832595">Roujo </a></td>
<td><a href="https://www.patreon.com/user?u=27956229">Oliver Maximilian Seidel</a></td>
<td><a href="https://www.patreon.com/weepjp">weepjp </a></td>
<td><a href="https://www.patreon.com/user?u=19045173">kiritan </a></td>
<td><a href="https://www.patreon.com/user?u=27648259">みなしま </a></td>
<td><a href="https://www.patreon.com/user?u=24430516">Eduardo Quiros</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/14215107/1cbe1912c26143919fa0faca16f12ce1/4.jpg?token-time=2145916800&token-hash=BslMqDjTjz8KYANLvxL87agHTugHa0dMPUzT-hwR6Vk%3D" alt="Nesakko" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/776209" alt="Demogrognard" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/3075183/c2ae575c604e420297f000ccc396e395/1.jpeg?token-time=2145916800&token-hash=O9qmPtpo6wWb0OuvnkEekhk_1WO2MTdytLr7ZgsAr80%3D" alt="Liaizon Wakest" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/23915207/25428766ecd745478e600b3d7f871eb2/1.png?token-time=2145916800&token-hash=urCLLA4KjJZX92Y1CxcBP4d8bVTHGkiaPnQZp-Tqz68%3D" alt="kabo2468y " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/8249688/4aacf36b6b244ab1bc6653591b6640df/2.png?token-time=2145916800&token-hash=1ZEf2w6L34253cZXS_HlVevLEENWS9QqrnxGUAYblPo%3D" alt="AureoleArk " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon " width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/36813045/29876ea679d443bcbba3c3f16edab8c2/2.jpeg?token-time=2145916800&token-hash=YCKWnIhrV9rjUCV9KqtJnEqjy_uGYF3WMXftjUdpi7o%3D" alt="Wataru Manji (manji0)" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/Nesakko">Nesakko</a></td>
<td><a href="https://www.patreon.com/user?u=776209">Demogrognard</a></td>
<td><a href="https://www.patreon.com/wakest">Liaizon Wakest</a></td>
<td><a href="https://www.patreon.com/user?u=557245">mkatze </a></td>
<td><a href="https://www.patreon.com/user?u=23915207">kabo2468y </a></td>
<td><a href="https://www.patreon.com/AureoleArk">AureoleArk </a></td>
<td><a href="https://www.patreon.com/osapon">osapon </a></td>
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ </a></td>
<td><a href="https://www.patreon.com/user?u=36813045">Wataru Manji (manji0)</a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61 " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5788159/af42076ab3354bb49803cfba65f94bee/1.jpg?token-time=2145916800&token-hash=iSaxp_Yr2-ZiU2YVi9rcpZZj9mj3UvNSMrZr4CU4qtA%3D" alt="mewl hayabusa" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/28779508/3cd4cb7f017f4ee0864341e3464d42f9/1.png?token-time=2145916800&token-hash=eGQtR15be44kgvh8fw2Jx8Db4Bv15YBp2ldxh0EKRxA%3D" alt="S Y" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/16542964" alt="Takumi Sugita" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/38837364/9421361c54c645ac8f5fc442a40c32e9/1.png?token-time=2145916800&token-hash=TUZB48Nem3BeUPLBH6s3P6WyKBnQOy0xKaDSTBBUNzA%3D" alt="xianon" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26340354/08834cf767b3449e93098ef73a434e2f/2.png?token-time=2145916800&token-hash=nyM8DnKRL8hR47HQ619mUzsqVRpkWZjgtgBU9RY15Uc%3D" alt="totokoro " width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61 </a></td>
<td><a href="https://www.patreon.com/hs_sh_net">mewl hayabusa</a></td>
<td><a href="https://www.patreon.com/user?u=28779508">S Y</a></td>
<td><a href="https://www.patreon.com/user?u=16542964">Takumi Sugita</a></td>
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin </a></td>
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
<td><a href="https://www.patreon.com/user?u=38837364">xianon</a></td>
<td><a href="https://www.patreon.com/user?u=26340354">totokoro </a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5827393/59893c191dda408f9cabd0f20a3a5627/1.jpeg?token-time=2145916800&token-hash=i9N05vOph-eP1LTLb9_npATjYOpntL0ZsHNaZFSsPmE%3D" alt="motcha " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/20494440/540beaf2445f408ea6597bc61e077bb3/1.png?token-time=2145916800&token-hash=UJ0JQge64Bx9XmN_qYA1inMQhrWf4U91fqz7VAKJeSg%3D" alt="axtuki1 " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpg?token-time=2145916800&token-hash=nVAntpybQrznE0rg05keLrSE6ogPKJXB13rmrJng42c%3D" alt="takimura " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13100201/fc5be4fa90444f09a9c8a06f72385272/1.png?token-time=2145916800&token-hash=i8PjlgfOB2LPEdbtWyx8ZPsBKhGcNZqcw_FQmH71UGU%3D" alt="aqz tamaina" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9109588/e3cffc48d20a4e43afe04123e696781d/3.png?token-time=2145916800&token-hash=T_VIUA0IFIbleZv4pIjiszZGnQonwn34sLCYFIhakBo%3D" alt="nafuchoco " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/26144593/9514b10a5c1b42a3af58621aee213d1d/1.png?token-time=2145916800&token-hash=v1PYRsjzu4c_mndN4Hvi_dlispZJsuGRCQeNS82pUSM%3D" alt="EBISUME" width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo " width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s </a></td>
<td><a href="https://www.patreon.com/user?u=5827393">motcha </a></td>
<td><a href="https://www.patreon.com/user?u=20494440">axtuki1 </a></td>
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
<td><a href="https://www.patreon.com/takimura">takimura </a></td>
<td><a href="https://www.patreon.com/aqz">aqz tamaina</a></td>
<td><a href="https://www.patreon.com/user?u=9109588">nafuchoco </a></td>
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
<td><a href="https://www.patreon.com/user?u=26144593">EBISUME</a></td>
<td><a href="https://www.patreon.com/noellabo">noellabo </a></td>
</tr></table>
<table><tr>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpg?token-time=2145916800&token-hash=7bkMqTwHPRsJPGAq42PYdDXDZBVGLqdgr1ZmBxX8GFQ%3D" alt="Hekovic " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/24641572/b4fd175424814f15b0ca9178d2d2d2e4/1.png?token-time=2145916800&token-hash=e2fyqdbuJbpCckHcwux7rbuW6OPkKdERcus0u2wIEWU%3D" alt="uroco @99" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/14661394" alt="Chandler " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
<td><img src="https://c8.patreon.com/2/200/23932002" alt="nenohi " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/9481273/7fa89168e72943859c3d3c96e424ed31/4.jpeg?token-time=2145916800&token-hash=5w1QV1qXe-NdWbdFmp1H7O_-QBsSiV0haumk3XTHIEg%3D" alt="Efertone " width="100"></td>
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
</tr><tr>
<td><a href="https://www.patreon.com/Corset">CG </a></td>
<td><a href="https://www.patreon.com/hekovic">Hekovic </a></td>
<td><a href="https://www.patreon.com/user?u=24641572">uroco @99</a></td>
<td><a href="https://www.patreon.com/user?u=14661394">Chandler </a></td>
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
<td><a href="https://www.patreon.com/user?u=23932002">nenohi </a></td>
<td><a href="https://www.patreon.com/efertone">Efertone </a></td>
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
</tr></table>
**Last updated:** Sun, 26 Jul 2020 07:00:10 UTC
<!-- PATREON_END -->
[backer-url]: #backers
[backer-badge]: https://opencollective.com/misskey/backers/badge.svg
[backers-image]: https://opencollective.com/misskey/backers.svg
[sponsor-url]: #sponsors
[sponsor-badge]: https://opencollective.com/misskey/sponsors/badge.svg
[sponsors-image]: https://opencollective.com/misskey/sponsors.svg
[support-url]: https://opencollective.com/misskey#support
[syuilo-link]: https://syuilo.com
[syuilo-icon]: https://avatars2.githubusercontent.com/u/4439005?v=3&s=70

View File

@ -1,36 +0,0 @@
# Roadmap
The order of individual tasks is a guide only and is subject to change depending on the situation.
Also, the later tasks are more indefinite and are subject to change as development progresses.
## (1) Improve maintainability \<current phase\>
This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development.
- Make the number of type errors zero (backend)
- Probably need to switch some libraries to others that make it difficult to reduce type errors
- e.g. koa to fastify https://github.com/misskey-dev/misskey/issues/7537
- Improve CI
- Fix tests
- mocha, jest, etc. do not support the combination of `TypeScript + ESM + Path alias`, and the tests currently do not work.
- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
- Add more tests
- May need to implement a mechanism that allows for DI
- Improve documentation
## (2) Improve functionality
Once Phase 1 is complete and an environment conducive to the development of a stable system is in place, the implementation of new functions can begin gradually.
- OAuth2 support https://github.com/misskey-dev/misskey/issues/8262
- GraphQL support?
## (3) Improve scalability
Once the development of the feature has settled down, this may be an opportunity to make larger modifications.
- Rewriting in Rust?
## (4) Change the world
It is time to promote Misskey and change the world.
- Become more major than services such as Twitter and become critical infrastructure for the world
- MiOS will be developed and integrated into various systems - What is MiOS?
- Letting Ai-chan interfere with the real world
- Make Misskey a member of GAFA; Misskey's office must be a reinforced concrete brutalist building with a courtyard.

BIN
assets/about/drive.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
assets/about/post.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

BIN
assets/about/reaction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
assets/about/ui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
assets/ai-orig.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
assets/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

BIN
assets/banner.afdesign Normal file

Binary file not shown.

BIN
assets/mi-white.afdesign Normal file

Binary file not shown.

BIN
assets/mi.afdesign Normal file

Binary file not shown.

BIN
assets/ss/explore.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
assets/ss/user.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

BIN
assets/title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -1,31 +1,52 @@
version: "3"
services:
db:
restart: always
image: postgres:12.2-alpine
ports:
- "5432:5432"
networks:
- internal_network
- external_network
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
redis:
restart: always
image: redis:4.0-alpine
ports:
- "6379:6379"
networks:
- internal_network
- external_network
volumes:
- ./redis:/data
networks:
internal_network:
internal: true
external_network:
version: "3"
services:
web:
build: .
restart: always
links:
- db
- redis
# - es
ports:
- "127.0.0.1:3000:3000"
networks:
- internal_network
- external_network
volumes:
- ./files:/misskey/files
- ./.config:/misskey/.config:ro
redis:
restart: always
image: redis:4.0-alpine
networks:
- internal_network
volumes:
- ./redis:/data
db:
restart: always
image: postgres:12.2-alpine
networks:
- internal_network
env_file:
- .config/docker.env
volumes:
- ./db:/var/lib/postgresql/data
# es:
# restart: always
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
# environment:
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
# - "TAKE_FILE_OWNERSHIP=111"
# networks:
# - internal_network
# volumes:
# - ./elasticsearch:/usr/share/elasticsearch/data
networks:
internal_network:
internal: true
external_network:

View File

@ -1,6 +0,0 @@
`packages/backend/src/server/web/views/base.pug` の中の
```
meta(name='theme-color' content='#好きな色')
meta(name='theme-color-orig' content='#好きな色')
```
の好きな色の部分を変更する(Hex値)

View File

@ -1,15 +0,0 @@
## 主な変更点
- なとナがにゃ
- コピーがコビー
- あなたが僕
- パスワードが鍵
- doneがドネ
- ユーザー名が名前
- バナーがバニャーニャ
- ユーザーネームがid
- コントロールパネルがコンソトールパネル
## ぬるきゃごの導入方法
1. `./locales/ja-JP.yml``ja-NCAT.yml` に変更
2. `ja-NCAT.yml``./locales` のなかに入れる
3. `./locales/inde.js``const languages``ja-NCAT` を追加する

View File

@ -189,7 +189,7 @@ clearCachedFiles: "امسح التخزين المؤقت"
clearCachedFilesConfirm: "أتريد حذف التخزين المؤقت للملفات البعيدة؟"
blockedInstances: "المثلاء المحجوبون"
blockedInstancesDescription: "قائمة بالمثلاء التي تريد حظرها بحيث كل نطاق في سطر لوحده. بعد إدراجهم لن يتمكنوا من التفاعل مع هذا المثيل."
muteAndBlock: "المكتومون والمحجوبون"
muteAndBlock: "تم كتمها / تم حجبها"
mutedUsers: "الحسابات المكتومة"
blockedUsers: "الحسابات المحجوبة"
noUsers: "ليس هناك مستخدمون"
@ -490,7 +490,7 @@ none: "لا شيء"
showInPage: "اعرض في الصفحة"
popout: "منبثقة"
volume: "مستوى الصوت"
masterVolume: "حجم الصوت الرئيس"
masterVolume: "القرص الرئيسي"
details: "التفاصيل"
chooseEmoji: "اختر إيموجي"
unableToProcess: "يتعذر إكمال العملية"
@ -521,7 +521,6 @@ divider: "فاصل"
addItem: "إضافة عنصر"
relays: "المُرَحلات"
addRelay: "إضافة مُرحّل"
inboxUrl: "رابط صندوق الوارد"
addedRelays: "المرحلات المضافة"
serviceworkerInfo: "يجب أن يفعل لإرسال الإشعارات."
deletedNote: "ملاحظة محذوفة"
@ -534,8 +533,6 @@ enablePlayer: "افتح مشغل الفيديو"
disablePlayer: "أغلق مشغل الفيديو"
themeEditor: "مصمم القوالب"
description: "الوصف"
describeFile: "أضف تعليقًا توضيحيًا"
enterFileDescription: "أدخل تعليقًا توضيحيًا"
author: "الكاتب"
leaveConfirm: "لديك تغييرات غير محفوظة. أتريد المتابعة دون حفظها؟"
manage: "إدارة "
@ -567,9 +564,6 @@ smtpPass: "الكلمة السرية"
emptyToDisableSmtpAuth: "اترك اسم المستخدم وكلمة المرور فارغين لتعطيل التحقق من SMTP"
smtpSecureInfo: "عطل هذا الخيار عند استخدام STARTTLS"
wordMute: "حظر الكلمات"
regexpError: "خطأ في التعبير النمطي"
instanceMute: "المثلاء المكتومون"
userSaysSomething: "كتب {name} شيءً"
makeActive: "تفعيل"
display: "المظهر"
copy: "نسخ"
@ -596,16 +590,10 @@ reportAbuse: "أبلغ"
reportAbuseOf: "أبلغ عن {name}"
fillAbuseReportDescription: "أكتب بالتفصيل سبب البلاغ، إذا كنت تبلغ عن ملاحظة أرفق رابط لها."
abuseReported: "أُرسل البلاغ، شكرًا لك"
reporter: "المُبلّغ"
reporteeOrigin: "أصل البلاغ"
reporterOrigin: "أصل المُبلّغ"
forwardReport: "وجّه البلاغ إلى المثيل البعيد"
forwardReportIsAnonymous: "في المثيل البعيد سيظهر المبلّغ كحساب مجهول."
send: "أرسل"
abuseMarkAsResolved: "علّم البلاغ كمحلول"
openInNewTab: "افتح في لسان جديد"
defaultNavigationBehaviour: "سلوك الملاحة الافتراضي"
editTheseSettingsMayBreakAccount: "تعديل هذه الإعدادات قد يسبب عطبًا لحسابك"
instanceTicker: "معلومات المثيل الأصلي للملاحظات"
waitingFor: "في انتظار {x}"
random: "عشوائي"
@ -636,15 +624,10 @@ no: "لا"
driveFilesCount: "عدد الملفات في قرص التخزين"
driveUsage: "المستغل من قرص التخزين"
noCrawleDescription: "يطلب من محركات البحث ألّا يُفهرسوا ملفك الشخصي وملاحظات وصفحاتك وما شابه."
alwaysMarkSensitive: "علّم افتراضيًا جميع ملاحظاتي كذات محتوى حساس"
loadRawImages: "حمّل الصور الأصلية بدلًا من المصغرات"
disableShowingAnimatedImages: "لا تشغّل الصور المتحركة"
verificationEmailSent: "أُرسل بريد التحقق. أنقر على الرابط المضمن لإكمال التحقق."
notSet: "لم يعيّن"
emailVerified: "تُحقّق من بريدك الإلكتروني"
noteFavoritesCount: "عدد الملاحظات المفضلة"
pageLikesCount: "عدد الصفحات التي أعجبت بها"
pageLikedCount: "عدد صفحاتك المُعجب بها"
contact: "التواصل"
useSystemFont: "استخدم الخط الافتراضية للنظام"
clips: "مشابك"
@ -652,7 +635,6 @@ experimentalFeatures: "ميّزات اختبارية"
developer: "المطور"
makeExplorable: "أظهر الحساب في صفحة \"استكشاف\""
makeExplorableDescription: "بتعطيل هذا الخيار لن يظهر حسابك في صفحة \"استكشاف\""
showGapBetweenNotesInTimeline: "أظهر فجوات بين المشاركات في الخيط الزمني"
wide: "عريض"
narrow: "رفيع"
reloadToApplySetting: "سيُطبق هذا الإعداد بعد إعادة تحميل الصفحة، أتريد إعادة تحميلها الآن؟"
@ -800,7 +782,6 @@ tenMinutes: "10 دقائق"
oneHour: "ساعة"
oneDay: "يوم"
oneWeek: "أسبوع"
failedToFetchAccountInformation: "تعذر جلب معلومات الحساب"
_emailUnavailable:
used: "هذا البريد الإلكتروني مستخدم"
format: "صيغة البريد الإلكتروني غير صالحة"
@ -879,7 +860,6 @@ _mfm:
centerDescription: "يمركز المحتوى في الوَسَط."
quote: "اقتبس"
emoji: "إيموجي مخصص"
emojiDescription: "إحاطة اسم الإيموجي بنقطتي تفسير سيستبدله بصورة الإيموجي."
search: "البحث"
flip: "اقلب"
flipDescription: "يقلب المحتوى عموديًا أو أفقيًا"
@ -891,28 +871,15 @@ _mfm:
jumpDescription: "يمنح للمحتوى حركة قفز."
bounce: "تأثير (ارتداد)"
bounceDescription: "يمنح للمحتوى حركة ارتدادية"
shake: "تأثير (اهتزاز)"
shakeDescription: "يمنح المحتوى حركة اهتزازية."
spin: "تأثير (دوران)"
spinDescription: "يمنح المحتوى حركة دورانية."
x2: "كبير"
x2Description: "يُكبر المحتوى"
x3: "كبير جداً"
x3Description: "يُضخم المحتوى"
x4: "هائل"
x4Description: "يُضخم المحتوى أكثر مما سبق."
blur: "طمس"
blurDescription: "يطمس المحتوى، لكن بالتمرير فوقه سيظهر بوضوح."
font: "الخط"
fontDescription: "الخط المستخدم لعرض المحتوى."
rainbow: "قوس قزح"
rainbowDescription: "اجعل المحتوى يظهر بألوان الطيف"
rotate: "تدوير"
rotateDescription: "يُدير المحتوى بزاوية معيّنة."
_instanceTicker:
none: "لا تظهره بتاتًا"
remote: "أظهر للمستخدمين البِعاد"
always: "أظهره دائمًا"
_serverDisconnectedBehavior:
reload: "إعادة تحميل تلقائية"
dialog: "أظهر مربع حوار التحذيرات"
@ -932,18 +899,12 @@ _menuDisplay:
hide: "إخفاء"
_wordMute:
muteWords: "الكلمات المحظورة"
muteWordsDescription: "افصل بينهم بمسافة لاستخدام معامل \"و\" أو بسطر لاستخدام معامل \"أو\"."
muteWordsDescription2: "احصر الكلمات المفتاحية بين بين شرطتين مائلتين لاستخدامها كتعابير نمطية"
softDescription: "اخف الملاحظات التي تستوف الشروط من الخيط الزمني."
hardDescription: "اخف الملاحظات التي تستوف الشروط من الخيط الزمني.بالإضافة إلى أن هذه الملاحظات ستبقى مخفية حتى وإن تغيرت الشروط."
soft: "لينة"
hard: "قاسية"
mutedNotes: "الملاحظات المكتومة"
_instanceMute:
instanceMuteDescription: "هذه سيحجب كل ملاحظات الخوادم المحجوبة ومشاركاتها والردود على تلك الملاحظات حتى وإن كانت من خادم غير محجوب."
instanceMuteDescription2: "مدخلة لكل سطر"
title: "يخفي ملاحظات الخوادم المسرودة."
heading: "قائمة الخوادم المحجوبة"
_theme:
explore: "استكشف قوالب المظهر"
install: "تنصيب قالب"
@ -1237,8 +1198,6 @@ _pages:
font: "الخط"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
eyeCatchingImageSet: "عيّن صورة مصغّرة"
eyeCatchingImageRemove: "احذف صورة مصغّرة"
chooseBlock: "إضافة كتلة"
selectType: "اختر النوع"
enterVariableName: "أدخل اسم المتغيّر"
@ -1499,7 +1458,6 @@ _notification:
pollVote: "مصوِت شارك في الاستطلاع"
receiveFollowRequest: "طلبات المتابعة المتلقاة"
followRequestAccepted: "طلبات المتابعة المقبولة"
groupInvited: "دعوات الفريق"
app: "إشعارات التطبيقات المرتبطة"
_deck:
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا"

View File

@ -832,10 +832,6 @@ size: "আকার"
numberOfColumn: "কলামের সংখ্যা"
searchByGoogle: "গুগল"
indefinitely: "অনির্দিষ্ট"
tenMinutes: "১০ মিনিট"
oneHour: "১ ঘণ্টা"
oneDay: "একদিন"
oneWeek: "এক সপ্তাহ"
_emailUnavailable:
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"

View File

@ -12,7 +12,6 @@ gotIt: "Ho he entès!"
cancel: "Cancel·lar"
smtpUser: "Nom d'usuari"
smtpPass: "Contrasenya"
searchByGoogle: "Cercar"
_mfm:
search: "Cercar"
_sfx:

View File

@ -449,7 +449,6 @@ clearCache: "Vyprázdnit mezipaměť"
info: "Informace"
user: "Uživatelé"
administration: "Administrace"
searchByGoogle: "Vyhledávání"
_email:
_follow:
title: "Máte nového následovníka"

View File

@ -478,8 +478,8 @@ promote: "Werbung schalten"
numberOfDays: "Anzahl der Tage"
hideThisNote: "Diese Notiz verstecken"
showFeaturedNotesInTimeline: "Beliebte Notizen in der Chronik anzeigen"
objectStorage: "Object Storage"
useObjectStorage: "Object Storage verwenden"
objectStorage: "Objektspeicher"
useObjectStorage: "Objektspeicher verwenden"
objectStorageBaseUrl: "Basis-URL"
objectStorageBaseUrlDesc: "Die als Referenz verwendete URL. Verwendest du einen CDN oder Proxy, gib dessen URL an. Für S3 verwende 'https://<bucket>.s3.amazonaws.com'. Für GCS o.ä. verwende 'https://storage.googleapis.com/<bucket>'."
objectStorageBucket: "Bucket"
@ -827,7 +827,7 @@ overridedDeviceKind: "Gerätetyp"
smartphone: "Smartphone"
tablet: "Tablet"
auto: "Automatisch"
themeColor: "Farbe der Instanz-Information"
themeColor: "Instanzfarbe"
size: "Größe"
numberOfColumn: "Spaltenanzahl"
searchByGoogle: "Googlen"
@ -840,8 +840,6 @@ tenMinutes: "10 Minuten"
oneHour: "Eine Stunde"
oneDay: "Einen Tag"
oneWeek: "Eine Woche"
reflectMayTakeTime: "Es kann etwas dauern, bis sich dies widerspiegelt."
failedToFetchAccountInformation: "Benutzerkontoinformationen konnten nicht abgefragt werden"
_emailUnavailable:
used: "Diese Email-Adresse wird bereits verwendet"
format: "Das Format dieser Email-Adresse ist ungültig"

View File

@ -94,8 +94,6 @@ unfollow: "Unfollow"
followRequestPending: "Follow request pending"
enterEmoji: "Enter an emoji"
renote: "Renote"
renoteHomeOnly: "Renote to home only"
renoteFollowersOnly: "Renote to followers only"
unrenote: "Take back renote"
renoted: "Renoted."
cantRenote: "This post can't be renoted."
@ -829,7 +827,7 @@ overridedDeviceKind: "Device type"
smartphone: "Smartphone"
tablet: "Tablet"
auto: "Auto"
themeColor: "Instance Ticker Color"
themeColor: "Theme Color"
size: "Size"
numberOfColumn: "Number of columns"
searchByGoogle: "Google"
@ -842,8 +840,6 @@ tenMinutes: "10 minutes"
oneHour: "One hour"
oneDay: "One day"
oneWeek: "One week"
reflectMayTakeTime: "It may take some time for this to be reflected."
failedToFetchAccountInformation: "Could not fetch account information"
_emailUnavailable:
used: "This email address is already being used"
format: "The format of this email address is invalid"

1138
locales/eo-UY.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -762,7 +762,6 @@ muteThread: "Ocultar hilo"
unmuteThread: "Mostrar hilo"
ffVisibility: "Visibilidad de seguidores y seguidos"
hide: "Ocultar"
searchByGoogle: "Buscar"
indefinitely: "Sin límite de tiempo"
_ffVisibility:
public: "Publicar"

View File

@ -1216,7 +1216,7 @@ _poll:
votesCount: "{n} votes"
totalVotes: "{n} votes au total"
vote: "Voter"
showResult: "Voir résultats"
showResult: "Voir les résultats"
voted: "Déjà voté"
closed: "Terminé"
remainingDays: "{d} jours, {h} heures restantes"

View File

@ -592,7 +592,6 @@ smtpSecure: "Gunakan SSL/TLS implisit untuk koneksi SMTP"
smtpSecureInfo: "Matikan ini ketika menggunakan STARTTLS"
testEmail: "Tes pengiriman surel"
wordMute: "Bisukan kata"
regexpError: "Kesalahan ekspresi reguler"
instanceMute: "Bisuka instansi"
userSaysSomething: "{name} mengatakan sesuatu"
makeActive: "Aktifkan"
@ -826,20 +825,7 @@ overridedDeviceKind: "Tipe perangkat"
smartphone: "Ponsel"
tablet: "Tablet"
auto: "Otomatis"
themeColor: "Warna Tema"
size: "Ukuran"
numberOfColumn: "Jumlah per kolom"
searchByGoogle: "Penelusuran"
instanceDefaultLightTheme: "Bawaan instan tema terang"
instanceDefaultDarkTheme: "Bawaan instan tema gelap"
instanceDefaultThemeDescription: "Masukkan kode tema di format obyek."
mutePeriod: "Batas waktu bisu"
indefinitely: "Selamanya"
tenMinutes: "10 Menit"
oneHour: "1 Jam"
oneDay: "1 Hari"
oneWeek: "1 Bulan"
failedToFetchAccountInformation: "Gagal untuk mendapatkan informasi akun"
_emailUnavailable:
used: "Alamat surel ini telah digunakan"
format: "Format tidak valid."
@ -1612,7 +1598,6 @@ _notification:
youReceivedFollowRequest: "Kamu menerima permintaan mengikuti"
yourFollowRequestAccepted: "Permintaan mengikuti kamu telah diterima"
youWereInvitedToGroup: "Telah diundang ke grup"
pollEnded: "Hasil Kuesioner telah keluar"
_types:
all: "Semua"
follow: "Ikuti"
@ -1622,7 +1607,6 @@ _notification:
quote: "Kutip"
reaction: "Reaksi"
pollVote: "Memilih di angket"
pollEnded: "Jajak pendapat berakhir"
receiveFollowRequest: "Permintaan mengikuti diterima"
followRequestAccepted: "Permintaan mengikuti disetujui"
groupInvited: "Diundang ke grup"

View File

@ -19,10 +19,12 @@ const languages = [
'da-DK',
'de-DE',
'en-US',
'eo-UY',
'es-ES',
'fr-FR',
'id-ID',
'it-IT',
'ja-NY',
'ja-JP',
'ja-KS',
'kab-KAB',
@ -56,6 +58,7 @@ module.exports = Object.entries(locales)
const [lang] = k.split('-');
switch (k) {
case 'ja-JP': return v;
case 'ja-NY':
case 'ja-KS':
case 'en-US': return merge(locales['ja-JP'], v);
default: return merge(

View File

@ -802,7 +802,6 @@ leaveGroupConfirm: "Uscire da「{name}」?"
useDrawerReactionPickerForMobile: "Mostra sul drawer da dispositivo mobile"
welcomeBackWithName: "Bentornato/a, {name}"
clickToFinishEmailVerification: "Fai click su [{ok}] per completare la verifica dell'indirizzo email."
searchByGoogle: "Cerca"
indefinitely: "Non scade"
_emailUnavailable:
used: "Email già in uso"

File diff suppressed because it is too large Load Diff

View File

@ -655,7 +655,6 @@ global: "グローバル"
sent: "送信"
hashtags: "ハッシュタグ"
hide: "隠す"
searchByGoogle: "探す"
indefinitely: "無期限"
_ad:
back: "戻る"

1793
locales/ja-NY.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -55,7 +55,6 @@ accountInfo: "Talɣut n umiḍan"
emailNotification: "Ilɣa imayl"
selectAccount: "Fren amiḍan"
accounts: "Imiḍan"
searchByGoogle: "Nadi"
_email:
_follow:
title: "Yeṭṭafaṛ-ik·em-id"

View File

@ -59,7 +59,6 @@ remove: "ಅಳಿಸು"
smtpUser: "ಬಳಕೆಹೆಸರು"
smtpPass: "ಗುಪ್ತಪದ"
user: "ಬಳಕೆದಾರ"
searchByGoogle: "ಹುಡುಕು"
_email:
_follow:
title: "ಹಿಂಬಾಲಿಸಿದರು"

View File

@ -825,7 +825,6 @@ overridedDeviceKind: "장치 유형"
smartphone: "스마트폰"
tablet: "태블릿"
auto: "자동"
searchByGoogle: "검색"
indefinitely: "무기한"
_emailUnavailable:
used: "이 메일 주소는 사용중입니다"

View File

@ -119,23 +119,6 @@ unblock: "Deblokkeren"
suspend: "Opschorten"
unsuspend: "Heractiveren"
blockConfirm: "Weet je zeker dat je dit account wil blokkeren?"
unblockConfirm: "Ben je zeker dat je deze account wil blokkeren?"
suspendConfirm: "Ben je zeker dat je deze account wil suspenderen?"
unsuspendConfirm: "Ben je zeker dat je deze account wil opnieuw aanstellen?"
flagAsBot: "Markeer dit account als een robot."
flagAsBotDescription: "Als dit account van een programma wordt beheerd, zet deze vlag aan. Het aanzetten helpt andere ontwikkelaars om bijvoorbeeld onbedoelde feedback loops te doorbreken of om Misskey meer geschikt te maken."
flagAsCat: "Markeer dit account als een kat."
flagAsCatDescription: "Zet deze vlag aan als je wilt aangeven dat dit account een kat is."
flagShowTimelineReplies: "Toon antwoorden op de tijdlijn."
flagShowTimelineRepliesDescription: "Als je dit vlag aanzet, toont de tijdlijn ook antwoorden op andere en niet alleen jouw eigen notities."
autoAcceptFollowed: "Accepteer verzoeken om jezelf te volgen vanzelf als je de verzoeker al volgt."
addAccount: "Account toevoegen"
loginFailed: "Aanmelding mislukt."
showOnRemote: "Toon op de externe instantie."
general: "Algemeen"
wallpaper: "Achtergrond"
setWallpaper: "Achtergrond instellen"
removeWallpaper: "Achtergrond verwijderen"
searchWith: "Zoeken: {q}"
youHaveNoLists: "Je hebt geen lijsten"
followConfirm: "Weet je zeker dat je {name} wilt volgen?"
@ -222,8 +205,6 @@ resetAreYouSure: "Resetten?"
saved: "Opgeslagen"
messaging: "Chat"
upload: "Uploaden"
keepOriginalUploading: "Origineel beeld behouden."
keepOriginalUploadingDescription: "Bewaar de originele versie bij het uploaden van afbeeldingen. Indien uitgeschakeld, wordt bij het uploaden een alternatieve versie voor webpublicatie genereert."
fromDrive: "Van schijf"
fromUrl: "Van URL"
uploadFromUrl: "Uploaden vanaf een URL"
@ -264,36 +245,9 @@ renameFile: "Wijzig bestandsnaam"
folderName: "Mapnaam"
createFolder: "Map aanmaken"
renameFolder: "Map hernoemen"
deleteFolder: "Map verwijderen"
addFile: "Bestand toevoegen"
emptyDrive: "Jouw Drive is leeg."
emptyFolder: "Deze map is leeg"
unableToDelete: "Kan niet worden verwijderd"
inputNewFileName: "Voer een nieuwe naam in"
copyUrl: "URL kopiëren"
rename: "Hernoemen"
avatar: "Avatar"
banner: "Banner"
nsfw: "NSFW"
whenServerDisconnected: "Wanneer de verbinding met de server wordt onderbroken"
disconnectedFromServer: "Verbinding met de server onderbroken."
inMb: "in megabytes"
pinnedNotes: "Vastgemaakte notitie"
userList: "Lijsten"
aboutMisskey: "Over Misskey"
administrator: "Beheerder"
token: "Token"
securityKeyName: "Sleutelnaam"
registerSecurityKey: "Zekerheids-Sleutel registreren"
lastUsed: "Laatst gebruikt"
unregister: "Uitschrijven"
passwordLessLogin: "Inloggen zonder wachtwoord"
resetPassword: "Wachtwoord terugzetten"
newPasswordIs: "Het nieuwe wachtwoord is „{password}”."
reduceUiAnimation: "Verminder beweging in de UI"
share: "Delen"
notFound: "Niet gevonden"
cacheClear: "Cache verwijderen"
smtpHost: "Server"
smtpUser: "Gebruikersnaam"
smtpPass: "Wachtwoord"
@ -302,7 +256,6 @@ user: "Gebruikers"
muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken"
hide: "Verbergen"
searchByGoogle: "Zoeken"
_email:
_follow:
title: "volgde jou"

View File

@ -758,7 +758,6 @@ received: "Otrzymane"
hashtags: "Hashtag"
pubSub: "Konta Pub/Sub"
hide: "Ukryj"
searchByGoogle: "Szukaj"
indefinitely: "Nigdy"
_ffVisibility:
public: "Publikuj"

View File

@ -1,7 +1,6 @@
---
_lang_: "Português"
headlineMisskey: "Rede conectada por notas"
introMisskey: "Bem-vindo! Misskey é um serviço de microblogue descentralizado de código aberto.\nCria \"notas\" e partilha o que te ocorre com todos à tua volta. 📡\nCom \"reações\" podes também expressar logo o que sentes às notas de todos. 👍\nExploremos um novo mundo! 🚀"
monthAndDay: "{day}/{month}"
search: "Pesquisar"
notifications: "Notificações"
@ -23,7 +22,6 @@ otherSettings: "Outras configurações"
openInWindow: "Abrir numa janela"
profile: "Perfil"
timeline: "Timeline"
noAccountDescription: "Este usuário não tem uma descrição."
login: "Iniciar sessão"
loggingIn: "Iniciando sessão…"
logout: "Sair"
@ -31,12 +29,8 @@ signup: "Registrar-se"
uploading: "Enviando…"
save: "Guardar"
users: "Usuários"
addUser: "Adicionar usuário"
favorite: "Favoritar"
favorites: "Favoritar"
unfavorite: "Remover dos favoritos"
favorited: "Adicionado aos favoritos."
alreadyFavorited: "Já adicionado aos favoritos."
showMore: "Ver mais"
youGotNewFollower: "Você tem um novo seguidor"
followRequestAccepted: "Pedido de seguir aceito"
@ -67,7 +61,6 @@ pinnedNotes: "Post fixado"
smtpUser: "Nome de usuário"
smtpPass: "Senha"
user: "Usuários"
searchByGoogle: "Pesquisar"
_email:
_follow:
title: "Você tem um novo seguidor"

View File

@ -449,56 +449,6 @@ groupInvited: "Ai fost invitat într-un grup"
aboutX: "Despre {x}"
useOsNativeEmojis: "Folosește emojiuri native OS-ului"
disableDrawer: "Nu folosi meniuri în stil sertar"
youHaveNoGroups: "Nu ai niciun grup"
joinOrCreateGroup: "Primește o invitație într-un grup sau creează unul nou."
noHistory: "Nu există istoric"
signinHistory: "Istoric autentificări"
disableAnimatedMfm: "Dezactivează MFM cu animații"
doing: "Se procesează..."
category: "Categorie"
tags: "Etichete"
docSource: "Sursa acestui document"
createAccount: "Creează un cont"
existingAccount: "Cont existent"
regenerate: "Regenerează"
fontSize: "Mărimea fontului"
noFollowRequests: "Nu ai nicio cerere de urmărire în așteptare"
openImageInNewTab: "Deschide imaginile în taburi noi"
dashboard: "Panou de control"
local: "Local"
remote: "Extern"
total: "Total"
weekOverWeekChanges: "Schimbări până săptămâna trecută"
dayOverDayChanges: "Schimbări până ieri"
appearance: "Aspect"
clientSettings: "Setări client"
accountSettings: "Setări cont"
promotion: "Promovat"
promote: "Promovează"
numberOfDays: "Numărul zilelor"
hideThisNote: "Ascunde această notă"
showFeaturedNotesInTimeline: "Arată notele recomandate în cronologii"
objectStorage: "Object Storage"
useObjectStorage: "Folosește Object Storage"
objectStorageBaseUrl: "URL de bază"
objectStorageBaseUrlDesc: "URL-ul este folosit pentru referință. Specifică URL-ul CDN-ului sau Proxy-ului tău dacă folosești unul. Pentru S3 folosește 'https://<bucket>.s3.amazonaws.com' și pentru GCS sau servicii echivalente folosește 'https://storage.googleapis.com/<bucket>', etc."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "Lasă acest câmp gol dacă folosești AWS S3, dacă nu specifică endpoint-ul ca '<host>' sau '<host>:<port>', depinzând de ce serviciu folosești."
objectStorageRegion: "Regiune"
objectStorageRegionDesc: "Specifică o regiune precum 'xx-east-1'. Dacă serviciul tău nu face distincția între regiuni lasă acest câmp gol sau introdu 'us-east-1'."
objectStorageUseSSL: "Folosește SSl"
objectStorageUseSSLDesc: "Oprește această opțiune dacă nu vei folosi HTTPS pentru conexiunile API-ului"
objectStorageUseProxy: "Conectează-te prin Proxy"
objectStorageUseProxyDesc: "Oprește această opțiune dacă vei nu folosi un Proxy pentru conexiunile API-ului"
objectStorageSetPublicRead: "Setează \"public-read\" pentru încărcare"
serverLogs: "Loguri server"
deleteAll: "Șterge tot"
showFixedPostForm: "Arată caseta de postare în vârful cronologie"
newNoteRecived: "Sunt note noi"
sounds: "Sunete"
listen: "Ascultă"
none: "Nimic"
@ -521,55 +471,12 @@ sort: "Sortează"
ascendingOrder: "Crescător"
descendingOrder: "Descrescător"
scratchpad: "Scratchpad"
scratchpadDescription: "Scratchpad-ul oferă un mediu de experimentare în AiScript. Poți scrie, executa și verifica rezultatele acestuia interacționând cu Misskey în el."
output: "Ieșire"
script: "Script"
disablePagesScript: "Dezactivează AiScript în Pagini"
updateRemoteUser: "Actualizează informațiile utilizatorului extern"
deleteAllFiles: "Șterge toate fișierele"
deleteAllFilesConfirm: "Ești sigur că vrei să ștergi toate fișierele?"
removeAllFollowing: "Dezurmărește toți utilizatorii urmăriți"
removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}. Te rog execută asta numai dacă instanța, de ex., nu mai există."
userSuspended: "Acest utilizator a fost suspendat."
userSilenced: "Acest utilizator a fost setat silențios."
yourAccountSuspendedTitle: "Acest cont a fost suspendat"
yourAccountSuspendedDescription: "Acest cont a fost suspendat din cauza încălcării termenilor de serviciu al serverului sau ceva similar. Contactează administratorul dacă ai dori să afli un motiv mai detaliat. Te rog nu crea un cont nou."
menu: "Meniu"
divider: "Separator"
addItem: "Adaugă element"
relays: "Relee"
addRelay: "Adaugă Releu"
inboxUrl: "URL-ul inbox-ului"
addedRelays: "Relee adăugate"
serviceworkerInfo: "Trebuie să fie activat pentru notificări push."
deletedNote: "Notă ștearsă"
invisibleNote: "Note ascunse"
enableInfiniteScroll: "Încarcă mai mult automat"
visibility: "Vizibilitate"
poll: "Sondaj"
useCw: "Ascunde conținutul"
enablePlayer: "Deschide player-ul video"
disablePlayer: "Închide player-ul video"
expandTweet: "Expandează tweet"
themeEditor: "Editor de teme"
description: "Descriere"
describeFile: "Adaugă titrări"
enterFileDescription: "Introdu titrările"
author: "Autor"
leaveConfirm: "Ai schimbări nesalvate. Vrei să renunți la ele?"
manage: "Gestionare"
plugins: "Pluginuri"
deck: "Deck"
undeck: "Părăsește Deck"
useBlurEffectForModal: "Folosește efect de blur pentru modale"
smtpHost: "Gazdă"
smtpUser: "Nume de utilizator"
smtpPass: "Parolă"
clearCache: "Golește cache-ul"
info: "Despre"
user: "Utilizatori"
administration: "Gestionare"
searchByGoogle: "Caută"
_email:
_follow:
title: "te-a urmărit"
@ -579,11 +486,9 @@ _mfm:
emoji: "Emoji personalizat"
search: "Caută"
_theme:
description: "Descriere"
keys:
mention: "Mențiune"
renote: "Re-notează"
divider: "Separator"
_sfx:
note: "Note"
notification: "Notificări"

View File

@ -815,7 +815,6 @@ leaveGroupConfirm: "Покинуть группу «{name}»?"
useDrawerReactionPickerForMobile: "Выдвижная палитра на мобильном устройстве"
welcomeBackWithName: "С возвращением, {name}!"
clickToFinishEmailVerification: "Пожалуйста, нажмите [{ok}], чтобы завершить подтверждение адреса электронной почты."
searchByGoogle: "Поиск"
indefinitely: "вечно"
_emailUnavailable:
used: "Уже используется"

View File

@ -839,8 +839,6 @@ tenMinutes: "10 minút"
oneHour: "1 hodina"
oneDay: "1 deň"
oneWeek: "1 týždeň"
reflectMayTakeTime: "Zmeny môžu chvíľu trvať kým sa prejavia."
failedToFetchAccountInformation: "Nepodarilo sa načítať informácie o účte."
_emailUnavailable:
used: "Táto emailová adresa sa už používa"
format: "Formát emailovej adresy je nesprávny"

View File

@ -47,7 +47,6 @@ remove: "Sil"
smtpUser: "Kullanıcı Adı"
smtpPass: "Şifre"
user: "Kullanıcı"
searchByGoogle: "Arama"
_mfm:
search: "Arama"
_sfx:

View File

@ -1,6 +1,5 @@
---
_lang_: "ياپونچە"
search: "ئىزدەش"
searchByGoogle: "ئىزدەش"
_mfm:
search: "ئىزدەش"

View File

@ -685,7 +685,6 @@ global: "Глобальна"
sent: "Відправити"
hashtags: "Хештеґ"
hide: "Сховати"
searchByGoogle: "Пошук"
indefinitely: "Ніколи"
_ad:
back: "Назад"

View File

@ -8,12 +8,12 @@ notifications: "通知"
username: "用户名"
password: "密码"
forgotPassword: "忘记密码"
fetchingAsApObject: "在联邦宇宙查询中..."
fetchingAsApObject: "联合查询"
ok: "OK"
gotIt: "我明白了"
cancel: "取消"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转"
renotedBy: "由 {user} 转"
noNotes: "没有帖文"
noNotifications: "无通知"
instance: "实例"
@ -69,7 +69,7 @@ exportRequested: "导出请求已提交,这可能需要花一些时间,导
importRequested: "导入请求已提交,这可能需要花一点时间。"
lists: "列表"
noLists: "列表为空"
note: "帖"
note: "帖"
notes: "帖子"
following: "关注中"
followers: "关注者"
@ -85,7 +85,7 @@ serverIsDead: "服务器没有响应。 请稍等片刻,然后重试。"
youShouldUpgradeClient: "请重新加载并使用新版本的客户端查看此页面。"
enterListName: "输入列表名称"
privacy: "隐私"
makeFollowManuallyApprove: "关注请求需要批准"
makeFollowManuallyApprove: "关注者的关注请求需要批准"
defaultNoteVisibility: "默认可见性"
follow: "关注"
followRequest: "关注申请"
@ -143,7 +143,7 @@ flagAsCat: "将这个账户设定为一只猫"
flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。"
flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许关注者的关注"
autoAcceptFollowed: "自动允许关注"
addAccount: "添加账户"
loginFailed: "登录失败"
showOnRemote: "转到所在实例显示"
@ -162,7 +162,7 @@ recipient: "收件人"
annotation: "注解"
federation: "联合"
instances: "实例"
registeredAt: "初次观"
registeredAt: "初次观"
latestRequestSentAt: "上次发送的请求"
latestRequestReceivedAt: "上次收到的请求"
latestStatus: "最后状态"
@ -254,7 +254,7 @@ agreeTo: "{0}人同意"
tos: "服务条款"
start: "开始"
home: "首页"
remoteUserCaution: "由于此用户来自其它实例,显示的信息可能不完整。"
remoteUserCaution: "由于是远程用户,信息不完整。"
activity: "活动"
images: "图片"
birthday: "生日"
@ -372,7 +372,7 @@ recentlyUpdatedUsers: "最近投稿的用户"
recentlyRegisteredUsers: "最近登录的用户"
recentlyDiscoveredUsers: "最近发现的用户"
exploreUsersCount: "有{count}个用户"
exploreFediverse: "探索联邦宇宙"
exploreFediverse: "探索Fediverse"
popularTags: "热门标签"
userList: "列表"
about: "关于"
@ -561,7 +561,7 @@ manage: "管理"
plugins: "插件"
deck: "Deck"
undeck: "取消Deck"
useBlurEffectForModal: "对话框使用模糊效果"
useBlurEffectForModal: "模态框使用模糊效果"
useFullReactionPicker: "使用全功能的回应工具栏"
width: "宽度"
height: "高度"
@ -840,8 +840,6 @@ tenMinutes: "10分钟"
oneHour: "1小时"
oneDay: "1天"
oneWeek: "1周"
reflectMayTakeTime: "可能需要一些时间才能体现出效果。"
failedToFetchAccountInformation: "获取账户信息失败"
_emailUnavailable:
used: "已经被使用过"
format: "无效的格式"
@ -906,7 +904,7 @@ _nsfw:
_mfm:
cheatSheet: "MFM代码速查表"
intro: "MFM是一种在Misskey中的各个位置使用的专用标记语言。在这里您可以看到MFM中可用的语法列表。"
dummy: "通过Misskey扩展联邦宇宙的世界"
dummy: "通过Misskey扩展Fediverse的世界"
mention: "提及"
mentionDescription: "可以使用 @+用户名 来指示特定用户"
hashtag: "话题标签"
@ -969,7 +967,7 @@ _mfm:
rotateDescription: "旋转指定的角度。"
_instanceTicker:
none: "不显示"
remote: "仅远程用户"
remote: "仅显示远程用户"
always: "始终显示"
_serverDisconnectedBehavior:
reload: "自动重载"
@ -1053,7 +1051,7 @@ _theme:
mention: "提及"
mentionMe: "提及"
renote: "转发"
modalBg: "对话框背景"
modalBg: "模态框背景"
divider: "分割线"
scrollbarHandle: "滚动条"
scrollbarHandleHover: "滚动条(悬停)"
@ -1240,7 +1238,7 @@ _visibility:
publicDescription: "您的帖子将出现在全局时间线上"
home: "首页"
homeDescription: "仅发送至首页的时间线"
followers: "关注者"
followers: "关注者"
followersDescription: "仅发送至关注者"
specified: "指定用户"
specifiedDescription: "仅发送至指定用户"

View File

@ -81,8 +81,6 @@ somethingHappened: "發生錯誤"
retry: "重試"
pageLoadError: "載入頁面失敗"
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
serverIsDead: "伺服器沒有回應。請稍等片刻,然後重試。"
youShouldUpgradeClient: "請重新載入以使用新版本的客戶端顯示此頁面"
enterListName: "輸入清單名稱"
privacy: "隱私"
makeFollowManuallyApprove: "手動審核追隨請求"
@ -106,7 +104,6 @@ clickToShow: "按一下以顯示"
sensitive: "敏感內容"
add: "新增"
reaction: "情感"
reactionSetting: "在選擇器中顯示反應"
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
rememberNoteVisibility: "記住貼文可見性"
attachCancel: "移除附件"
@ -141,7 +138,6 @@ flagAsBot: "此使用者是機器人"
flagAsBotDescription: "如果本帳戶是由程式控制請啟用此選項。啟用後會作為標示幫助其他開發者防止機器人之間產生無限互動的行為並會調整Misskey內部系統將本帳戶識別為機器人"
flagAsCat: "此使用者是貓"
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
addAccount: "添加帳戶"
loginFailed: "登入失敗"
@ -603,9 +599,6 @@ reportAbuse: "檢舉"
reportAbuseOf: "檢舉{name}"
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話請附上針對的URL網址。"
abuseReported: "回報已送出。感謝您的報告。"
reporter: "檢舉者"
reporteeOrigin: "檢舉來源"
reporterOrigin: "檢舉者來源"
send: "發送"
abuseMarkAsResolved: "處理完畢"
openInNewTab: "在新分頁中開啟"
@ -741,7 +734,6 @@ postToGallery: "發佈到相簿"
gallery: "相簿"
recentPosts: "最新貼文"
popularPosts: "熱門的貼文"
shareWithNote: "在貼文中分享"
ads: "廣告"
expiration: "期限"
memo: "備忘錄"
@ -751,35 +743,13 @@ middle: "中"
low: "低"
emailNotConfiguredWarning: "沒有設定電子郵件地址"
ratio: "%"
previewNoteText: "預覽文本"
customCss: "自定義 CSS"
global: "公開"
sent: "發送"
received: "收取"
searchResult: "搜尋結果"
hashtags: "#tag"
troubleshooting: "故障排除"
useBlurEffect: "在 UI 上使用模糊效果"
misskeyUpdated: "Misskey 更新完成!"
translate: "翻譯"
translatedFrom: "從 {x} 翻譯"
accountDeletionInProgress: "正在刪除帳戶"
pubSub: "Pub/Sub 帳戶"
resolved: "已解決"
unresolved: "未解決"
breakFollow: "移除追蹤者"
hide: "隱藏"
leaveGroupConfirm: "確定離開「{name}」?"
auto: "自動"
searchByGoogle: "搜尋"
indefinitely: "無期限"
_ffVisibility:
public: "發佈"
private: "私密"
_signup:
almostThere: "即將完成"
_accountDelete:
inProgress: "正在刪除"
_ad:
back: "返回"
reduceFrequencyOfThisAd: "降低此廣告的頻率 "

View File

@ -1,10 +1,10 @@
{
"name": "misskey",
"version": "12.110.1-nca10.net-v1",
"version": "12.108.0-simkey",
"codename": "indigo",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
"url": "https://github.com/sim1222/misskey.git"
},
"private": true,
"scripts": {
@ -13,7 +13,8 @@
"start": "cd packages/backend && node --experimental-json-modules ./built/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js",
"init": "npm run migrate",
"migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js",
"ormconfig": "node ./packages/backend/ormconfig.js",
"migrate": "cd packages/backend && npx typeorm migration:run",
"migrateandstart": "npm run migrate && npm run start",
"gulp": "gulp build",
"watch": "npm run dev",
@ -41,10 +42,10 @@
"js-yaml": "4.1.0"
},
"devDependencies": {
"@typescript-eslint/parser": "5.18.0",
"@typescript-eslint/parser": "5.14.0",
"cross-env": "7.0.3",
"cypress": "9.5.3",
"cypress": "9.5.0",
"start-server-and-test": "1.14.0",
"typescript": "4.6.3"
"typescript": "4.6.2"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,19 +0,0 @@
export class webhook1648548247382 {
name = 'webhook1648548247382'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "webhook" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "name" character varying(128) NOT NULL, "on" character varying(128) array NOT NULL DEFAULT '{}', "url" character varying(1024) NOT NULL, "secret" character varying(1024) NOT NULL, "active" boolean NOT NULL DEFAULT true, CONSTRAINT "PK_e6765510c2d078db49632b59020" PRIMARY KEY ("id")); COMMENT ON COLUMN "webhook"."createdAt" IS 'The created date of the Antenna.'; COMMENT ON COLUMN "webhook"."userId" IS 'The owner ID.'; COMMENT ON COLUMN "webhook"."name" IS 'The name of the Antenna.'`);
await queryRunner.query(`CREATE INDEX "IDX_f272c8c8805969e6a6449c77b3" ON "webhook" ("userId") `);
await queryRunner.query(`CREATE INDEX "IDX_8063a0586ed1dfbe86e982d961" ON "webhook" ("on") `);
await queryRunner.query(`CREATE INDEX "IDX_5a056076f76b2efe08216ba655" ON "webhook" ("active") `);
await queryRunner.query(`ALTER TABLE "webhook" ADD CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "webhook" DROP CONSTRAINT "FK_f272c8c8805969e6a6449c77b3c"`);
await queryRunner.query(`DROP INDEX "public"."IDX_5a056076f76b2efe08216ba655"`);
await queryRunner.query(`DROP INDEX "public"."IDX_8063a0586ed1dfbe86e982d961"`);
await queryRunner.query(`DROP INDEX "public"."IDX_f272c8c8805969e6a6449c77b3"`);
await queryRunner.query(`DROP TABLE "webhook"`);
}
}

View File

@ -1,14 +0,0 @@
export class webhook21648816172177 {
name = 'webhook21648816172177'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "webhook" ADD "latestSentAt" TIMESTAMP WITH TIME ZONE`);
await queryRunner.query(`ALTER TABLE "webhook" ADD "latestStatus" integer`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestStatus"`);
await queryRunner.query(`ALTER TABLE "webhook" DROP COLUMN "latestSentAt"`);
}
}

View File

@ -1,8 +1,7 @@
import { DataSource } from 'typeorm';
import config from './built/config/index.js';
import { entities } from './built/db/postgre.js';
export default new DataSource({
export default {
type: 'postgres',
host: config.db.host,
port: config.db.port,
@ -12,4 +11,7 @@ export default new DataSource({
extra: config.db.extra,
entities: entities,
migrations: ['migration/*.js'],
});
cli: {
migrationsDir: 'migration'
}
};

View File

@ -3,9 +3,10 @@
"private": true,
"type": "module",
"scripts": {
"init": "npm run migrate",
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs",
"lint": "eslint --quiet \"src/**/*.ts\"",
"lint": "eslint --quiet src/**/*.ts",
"mocha": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "npm run mocha"
},
@ -14,11 +15,11 @@
"lodash": "^4.17.21"
},
"dependencies": {
"@discordapp/twemoji": "13.1.1",
"@discordapp/twemoji": "13.1.0",
"@elastic/elasticsearch": "7.11.0",
"@koa/cors": "3.1.0",
"@koa/multer": "3.0.0",
"@koa/router": "9.0.1",
"@koa/router": "10.1.1",
"@sinonjs/fake-timers": "9.1.1",
"@syuilo/aiscript": "0.11.1",
"@types/bcryptjs": "2.4.2",
@ -30,7 +31,7 @@
"@types/jsdom": "16.2.14",
"@types/jsonld": "1.5.6",
"@types/koa": "2.13.4",
"@types/koa-bodyparser": "4.3.7",
"@types/koa-bodyparser": "4.3.6",
"@types/koa-cors": "0.0.2",
"@types/koa-favicon": "2.0.21",
"@types/koa-logger": "3.1.2",
@ -41,7 +42,7 @@
"@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11",
"@types/mocha": "9.1.0",
"@types/node": "17.0.23",
"@types/node": "17.0.21",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.4",
"@types/oauth": "0.9.1",
@ -55,28 +56,28 @@
"@types/redis": "4.0.11",
"@types/rename": "1.0.4",
"@types/sanitize-html": "2.6.2",
"@types/sharp": "0.30.1",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/sharp": "0.29.5",
"@types/sinonjs__fake-timers": "8.1.1",
"@types/speakeasy": "2.0.7",
"@types/throttle-debounce": "2.1.0",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
"@types/uuid": "8.3.4",
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.18.0",
"@typescript-eslint/parser": "5.18.0",
"@bull-board/koa": "3.10.3",
"@types/ws": "8.5.2",
"@typescript-eslint/eslint-plugin": "5.14.0",
"@typescript-eslint/parser": "5.14.0",
"abort-controller": "3.0.0",
"ajv": "8.11.0",
"ajv": "8.10.0",
"archiver": "5.3.0",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
"aws-sdk": "2.1111.0",
"aws-sdk": "2.1079.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.5",
"broadcast-channel": "4.10.0",
"bull": "4.8.1",
"bull": "4.7.0",
"cacheable-lookup": "6.0.4",
"cafy": "15.2.1",
"cbor": "8.1.0",
@ -88,19 +89,19 @@
"date-fns": "2.28.0",
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
"eslint": "8.13.0",
"eslint-plugin-import": "2.26.0",
"eslint": "8.10.0",
"eslint-plugin-import": "2.25.4",
"feed": "4.2.2",
"file-type": "17.1.1",
"fluent-ffmpeg": "2.1.2",
"got": "12.0.3",
"got": "12.0.1",
"hpagent": "0.1.2",
"http-signature": "1.3.6",
"ip-cidr": "3.0.4",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
"jsdom": "19.0.0",
"json5": "2.2.1",
"json5": "2.2.0",
"json5-loader": "4.0.1",
"jsonld": "5.2.0",
"jsrsasign": "8.0.20",
@ -114,14 +115,14 @@
"koa-slow": "2.1.0",
"koa-views": "7.0.2",
"mfm-js": "0.21.0",
"mime-types": "2.1.35",
"mime-types": "2.1.34",
"misskey-js": "0.0.14",
"mocha": "9.2.2",
"mocha": "9.2.1",
"ms": "3.0.0-canary.1",
"multer": "1.4.4",
"nested-property": "4.0.0",
"node-fetch": "3.2.3",
"nodemailer": "6.7.3",
"node-fetch": "3.2.2",
"nodemailer": "6.7.2",
"os-utils": "0.0.14",
"parse5": "6.0.1",
"pg": "8.7.3",
@ -144,24 +145,24 @@
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sanitize-html": "2.7.0",
"semver": "7.3.6",
"sharp": "0.30.3",
"sharp": "0.30.2",
"speakeasy": "2.0.0",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"style-loader": "3.3.1",
"summaly": "2.5.0",
"syslog-pro": "1.0.0",
"systeminformation": "5.11.9",
"systeminformation": "5.11.6",
"throttle-debounce": "3.0.1",
"tinycolor2": "1.4.2",
"tmp": "0.2.1",
"ts-loader": "9.2.8",
"ts-loader": "9.2.7",
"ts-node": "10.7.0",
"tsc-alias": "1.4.1",
"tsconfig-paths": "3.14.1",
"twemoji-parser": "14.0.0",
"typeorm": "0.3.5",
"typescript": "4.6.3",
"tsconfig-paths": "3.13.0",
"twemoji-parser": "13.1.0",
"typeorm": "0.2.45",
"typescript": "4.6.2",
"ulid": "2.3.0",
"unzipper": "0.10.11",
"uuid": "8.3.2",
@ -171,7 +172,7 @@
"xev": "2.0.1"
},
"devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.93",
"@redocly/openapi-core": "1.0.0-beta.83",
"@types/fluent-ffmpeg": "2.1.20",
"cross-env": "7.0.3",
"execa": "6.1.0"

View File

@ -6,7 +6,7 @@ import cluster from 'node:cluster';
import chalk from 'chalk';
import chalkTemplate from 'chalk-template';
import * as portscanner from 'portscanner';
import semver from 'semver';
import { getConnection } from 'typeorm';
import Logger from '@/services/logger.js';
import loadConfig from '@/config/load.js';
@ -14,7 +14,7 @@ import { Config } from '@/config/types.js';
import { lessThan } from '@/prelude/array.js';
import { envOption } from '../env.js';
import { showMachineInfo } from '@/misc/show-machine-info.js';
import { db, initDb } from '../db/postgre.js';
import { initDb } from '../db/postgre.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@ -88,6 +88,10 @@ export async function masterMain() {
}
}
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
const requiredNodejsVersion = [11, 7, 0];
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
function showEnvironment(): void {
const env = process.env.NODE_ENV;
const logger = bootLogger.createSubLogger('env');
@ -104,11 +108,10 @@ function showEnvironment(): void {
function showNodejsVersion(): void {
const nodejsLogger = bootLogger.createSubLogger('nodejs');
nodejsLogger.info(`Version ${process.version} detected.`);
nodejsLogger.info(`Version ${runningNodejsVersion.join('.')}`);
const minVersion = fs.readFileSync(`${_dirname}/../../../../.node-version`, 'utf-8').trim();
if (semver.lt(process.version, minVersion)) {
nodejsLogger.error(`At least Node.js ${minVersion} required!`);
if (!satisfyNodejsVersion) {
nodejsLogger.error(`Node.js version is less than ${requiredNodejsVersion.join('.')}. Please upgrade it.`, null, true);
process.exit(1);
}
}
@ -143,7 +146,7 @@ async function connectDb(): Promise<void> {
try {
dbLogger.info('Connecting...');
await initDb();
const v = await db.query('SHOW server_version').then(x => x[0].server_version);
const v = await getConnection().query('SHOW server_version').then(x => x[0].server_version);
dbLogger.succ(`Connected: v${v}`);
} catch (e) {
dbLogger.error('Cannot connect', null, true);

View File

@ -2,10 +2,9 @@
import pg from 'pg';
pg.types.setTypeParser(20, Number);
import { Logger, DataSource } from 'typeorm';
import { createConnection, Logger, getConnection } from 'typeorm';
import * as highlight from 'cli-highlight';
import config from '@/config/index.js';
import { envOption } from '../env.js';
import { dbLogger } from './logger.js';
@ -62,6 +61,7 @@ import { Antenna } from '@/models/entities/antenna.js';
import { AntennaNote } from '@/models/entities/antenna-note.js';
import { PromoNote } from '@/models/entities/promo-note.js';
import { PromoRead } from '@/models/entities/promo-read.js';
import { envOption } from '../env.js';
import { Relay } from '@/models/entities/relay.js';
import { MutedNote } from '@/models/entities/muted-note.js';
import { Channel } from '@/models/entities/channel.js';
@ -73,9 +73,8 @@ import { PasswordResetRequest } from '@/models/entities/password-reset-request.j
import { UserPending } from '@/models/entities/user-pending.js';
import { entities as charts } from '@/services/chart/entities.js';
import { Webhook } from '@/models/entities/webhook.js';
const sqlLogger = dbLogger.createSubLogger('sql', 'gray', false);
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
class MyCustomLogger implements Logger {
private highlight(sql: string) {
@ -85,7 +84,9 @@ class MyCustomLogger implements Logger {
}
public logQuery(query: string, parameters?: any[]) {
sqlLogger.info(this.highlight(query).substring(0, 100));
if (envOption.verbose) {
sqlLogger.info(this.highlight(query));
}
}
public logQueryError(error: string, query: string, parameters?: any[]) {
@ -172,59 +173,58 @@ export const entities = [
Ad,
PasswordResetRequest,
UserPending,
Webhook,
...charts,
];
const log = process.env.NODE_ENV !== 'production';
export const db = new DataSource({
type: 'postgres',
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: {
statement_timeout: 1000 * 10,
...config.db.extra,
},
synchronize: process.env.NODE_ENV === 'test',
dropSchema: process.env.NODE_ENV === 'test',
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.pass,
prefix: `${config.redis.prefix}:query:`,
db: config.redis.db || 0,
},
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
maxQueryExecutionTime: 300,
entities: entities,
migrations: ['../../migration/*.js'],
});
export async function initDb() {
if (db.isInitialized) {
// nop
} else {
await db.connect();
export function initDb(justBorrow = false, sync = false, forceRecreate = false) {
if (!forceRecreate) {
try {
const conn = getConnection();
return Promise.resolve(conn);
} catch (e) {}
}
const log = process.env.NODE_ENV !== 'production';
return createConnection({
type: 'postgres',
host: config.db.host,
port: config.db.port,
username: config.db.user,
password: config.db.pass,
database: config.db.db,
extra: {
statement_timeout: 1000 * 10,
...config.db.extra,
},
synchronize: process.env.NODE_ENV === 'test' || sync,
dropSchema: process.env.NODE_ENV === 'test' && !justBorrow,
cache: !config.db.disableCache ? {
type: 'redis',
options: {
host: config.redis.host,
port: config.redis.port,
password: config.redis.pass,
prefix: `${config.redis.prefix}:query:`,
db: config.redis.db || 0,
},
} : false,
logging: log,
logger: log ? new MyCustomLogger() : undefined,
entities: entities,
});
}
export async function resetDb() {
const reset = async () => {
const tables = await db.query(`SELECT relname AS "table"
const conn = await getConnection();
const tables = await conn.query(`SELECT relname AS "table"
FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
WHERE nspname NOT IN ('pg_catalog', 'information_schema')
AND C.relkind = 'r'
AND nspname !~ '^pg_toast';`);
for (const table of tables) {
await db.query(`DELETE FROM "${table.table}" CASCADE`);
await conn.query(`DELETE FROM "${table.table}" CASCADE`);
}
};

View File

@ -1,5 +1,5 @@
export class Cache<T> {
public cache: Map<string | null, { date: number; value: T; }>;
private cache: Map<string | null, { date: number; value: T; }>;
private lifetime: number;
constructor(lifetime: Cache<never>['lifetime']) {
@ -28,52 +28,16 @@ export class Cache<T> {
this.cache.delete(key);
}
/**
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
*/
public async fetch(key: string | null, fetcher: () => Promise<T>, validator?: (cachedValue: T) => boolean): Promise<T> {
public async fetch(key: string | null, fetcher: () => Promise<T>): Promise<T> {
const cachedValue = this.get(key);
if (cachedValue !== undefined) {
if (validator) {
if (validator(cachedValue)) {
// Cache HIT
return cachedValue;
}
} else {
// Cache HIT
return cachedValue;
}
// Cache HIT
return cachedValue;
}
// Cache MISS
const value = await fetcher();
return value;
}
/**
* キャッシュがあればそれを返し、無ければfetcherを呼び出して結果をキャッシュ&返します
* optional: キャッシュが存在してもvalidatorでfalseを返すとキャッシュ無効扱いにします
*/
public async fetchMaybe(key: string | null, fetcher: () => Promise<T | undefined>, validator?: (cachedValue: T) => boolean): Promise<T | undefined> {
const cachedValue = this.get(key);
if (cachedValue !== undefined) {
if (validator) {
if (validator(cachedValue)) {
// Cache HIT
return cachedValue;
}
} else {
// Cache HIT
return cachedValue;
}
}
// Cache MISS
const value = await fetcher();
if (value !== undefined) {
this.set(key, value);
}
this.set(key, value);
return value;
}
}

View File

@ -42,8 +42,7 @@ async function getCaptchaResponse(url: string, secret: string, response: string)
headers: {
'User-Agent': config.userAgent,
},
// TODO
//timeout: 10 * 1000,
timeout: 10 * 1000,
agent: getAgentByUrl,
}).catch(e => {
throw `${e.message || e}`;

View File

@ -1,26 +1,17 @@
import { Antenna } from '@/models/entities/antenna.js';
import { Note } from '@/models/entities/note.js';
import { User } from '@/models/entities/user.js';
import { UserListJoinings, UserGroupJoinings, Blockings } from '@/models/index.js';
import { UserListJoinings, UserGroupJoinings } from '@/models/index.js';
import { getFullApAccount } from './convert-host.js';
import * as Acct from '@/misc/acct.js';
import { Packed } from './schema.js';
import { Cache } from './cache.js';
const blockingCache = new Cache<User['id'][]>(1000 * 60 * 5);
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
/**
* noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
*/
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
if (note.visibility === 'specified') return false;
// アンテナ作成者がノート作成者にブロックされていたらスキップ
const blockings = await blockingCache.fetch(noteUser.id, () => Blockings.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId)));
if (blockings.some(blocking => blocking === antenna.userId)) return false;
if (note.visibility === 'followers') {
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
@ -32,15 +23,15 @@ export async function checkHitAntenna(antenna: Antenna, note: (Note | Packed<'No
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
} else if (antenna.src === 'list') {
const listUsers = (await UserListJoinings.findBy({
const listUsers = (await UserListJoinings.find({
userListId: antenna.userListId!,
})).map(x => x.userId);
if (!listUsers.includes(note.userId)) return false;
} else if (antenna.src === 'group') {
const joining = await UserGroupJoinings.findOneByOrFail({ id: antenna.userGroupJoiningId! });
const joining = await UserGroupJoinings.findOneOrFail(antenna.userGroupJoiningId!);
const groupUsers = (await UserGroupJoinings.findBy({
const groupUsers = (await UserGroupJoinings.find({
userGroupId: joining.userGroupId,
})).map(x => x.userId);

View File

@ -6,7 +6,7 @@ import { httpAgent, httpsAgent, StatusError } from './fetch.js';
import config from '@/config/index.js';
import chalk from 'chalk';
import Logger from '@/services/logger.js';
import IPCIDR from 'ip-cidr';
import * as IPCIDR from 'ip-cidr';
import PrivateIp from 'private-ip';
const pipeline = util.promisify(stream.pipeline);

View File

@ -1,21 +1,19 @@
import { db } from '@/db/postgre.js';
import { Meta } from '@/models/entities/meta.js';
import { getConnection } from 'typeorm';
let cache: Meta;
export async function fetchMeta(noCache = false): Promise<Meta> {
if (!noCache && cache) return cache;
return await db.transaction(async transactionalEntityManager => {
return await getConnection().transaction(async transactionalEntityManager => {
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
const metas = await transactionalEntityManager.find(Meta, {
const meta = await transactionalEntityManager.findOne(Meta, {
order: {
id: 'DESC',
},
});
const meta = metas[0];
if (meta) {
cache = meta;
return meta;

View File

@ -5,5 +5,5 @@ import { Users } from '@/models/index.js';
export async function fetchProxyAccount(): Promise<ILocalUser | null> {
const meta = await fetchMeta();
if (meta.proxyAccountId == null) return null;
return await Users.findOneByOrFail({ id: meta.proxyAccountId }) as ILocalUser;
return await Users.findOneOrFail(meta.proxyAccountId) as ILocalUser;
}

View File

@ -120,9 +120,9 @@ export const httpsAgent = config.proxy
*/
export function getAgentByUrl(url: URL, bypassProxy = false) {
if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) {
return url.protocol === 'http:' ? _http : _https;
return url.protocol == 'http:' ? _http : _https;
} else {
return url.protocol === 'http:' ? httpAgent : httpsAgent;
return url.protocol == 'http:' ? httpAgent : httpsAgent;
}
}

View File

@ -23,7 +23,7 @@ export const getNoteSummary = (note: Packed<'Note'>): string => {
}
// ファイルが添付されているとき
if ((note.files || []).length !== 0) {
if ((note.files || []).length != 0) {
summary += ` (📎${note.files!.length})`;
}

View File

@ -6,5 +6,5 @@ import { Cache } from './cache.js';
const cache = new Cache<UserKeypair>(Infinity);
export async function getUserKeypair(userId: User['id']): Promise<UserKeypair> {
return await cache.fetch(userId, () => UserKeypairs.findOneByOrFail({ userId: userId }));
return await cache.fetch(userId, () => UserKeypairs.findOneOrFail(userId));
}

View File

@ -1,4 +1,4 @@
import { In, IsNull } from 'typeorm';
import { In } from 'typeorm';
import { Emojis } from '@/models/index.js';
import { Emoji } from '@/models/entities/emoji.js';
import { Note } from '@/models/entities/note.js';
@ -52,9 +52,9 @@ export async function populateEmoji(emojiName: string, noteUserHost: string | nu
const { name, host } = parseEmojiStr(emojiName, noteUserHost);
if (name == null) return null;
const queryOrNull = async () => (await Emojis.findOneBy({
const queryOrNull = async () => (await Emojis.findOne({
name,
host: host ?? IsNull(),
host,
})) || null;
const emoji = await cache.fetch(`${name} ${host}`, queryOrNull);
@ -112,7 +112,7 @@ export async function prefetchEmojis(emojis: { name: string; host: string | null
for (const host of hosts) {
emojisQuery.push({
name: In(notCachedEmojis.filter(e => e.host === host).map(e => e.name)),
host: host ?? IsNull(),
host: host,
});
}
const _emojis = emojisQuery.length > 0 ? await Emojis.find({

View File

@ -3,7 +3,6 @@ import { emojiRegex } from './emoji-regex.js';
import { fetchMeta } from './fetch-meta.js';
import { Emojis } from '@/models/index.js';
import { toPunyNullable } from './convert-host.js';
import { IsNull } from 'typeorm';
const legacies: Record<string, string> = {
'like': '👍',
@ -75,8 +74,8 @@ export async function toDbReaction(reaction?: string | null, reacterHost?: strin
const custom = reaction.match(/^:([\w+-]+)(?:@\.)?:$/);
if (custom) {
const name = custom[1];
const emoji = await Emojis.findOneBy({
host: reacterHost ?? IsNull(),
const emoji = await Emojis.findOne({
host: reacterHost || null,
name,
});

View File

@ -1,49 +0,0 @@
import { Webhooks } from '@/models/index.js';
import { Webhook } from '@/models/entities/webhook.js';
import { subsdcriber } from '../db/redis.js';
let webhooksFetched = false;
let webhooks: Webhook[] = [];
export async function getActiveWebhooks() {
if (!webhooksFetched) {
webhooks = await Webhooks.findBy({
active: true,
});
webhooksFetched = true;
}
return webhooks;
}
subsdcriber.on('message', async (_, data) => {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {
const { type, body } = obj.message;
switch (type) {
case 'webhookCreated':
if (body.active) {
webhooks.push(body);
}
break;
case 'webhookUpdated':
if (body.active) {
const i = webhooks.findIndex(a => a.id === body.id);
if (i > -1) {
webhooks[i] = body;
} else {
webhooks.push(body);
}
} else {
webhooks = webhooks.filter(a => a.id !== body.id);
}
break;
case 'webhookDeleted':
webhooks = webhooks.filter(a => a.id !== body.id);
break;
default:
break;
}
}
});

View File

@ -1,6 +1,6 @@
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
import { id } from '../id.js';
import { DriveFile } from './drive-file.js';
import { id } from '../id.js';
@Entity()
@Index(['usernameLower', 'host'], { unique: true })
@ -207,7 +207,7 @@ export class User {
@Column('boolean', {
default: false,
comment: 'Whether to show users replying to other users in the timeline',
comment: 'Whether to show users replying to other users in the timeline'
})
public showTimelineReplies: boolean;
@ -234,9 +234,3 @@ export interface ILocalUser extends User {
export interface IRemoteUser extends User {
host: string;
}
export type CacheableLocalUser = ILocalUser;
export type CacheableRemoteUser = IRemoteUser;
export type CacheableUser = CacheableLocalUser | CacheableRemoteUser;

View File

@ -1,73 +0,0 @@
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
import { User } from './user.js';
import { id } from '../id.js';
export const webhookEventTypes = ['mention', 'unfollow', 'follow', 'followed', 'note', 'reply', 'renote', 'reaction'] as const;
@Entity()
export class Webhook {
@PrimaryColumn(id())
public id: string;
@Column('timestamp with time zone', {
comment: 'The created date of the Antenna.',
})
public createdAt: Date;
@Index()
@Column({
...id(),
comment: 'The owner ID.',
})
public userId: User['id'];
@ManyToOne(type => User, {
onDelete: 'CASCADE',
})
@JoinColumn()
public user: User | null;
@Column('varchar', {
length: 128,
comment: 'The name of the Antenna.',
})
public name: string;
@Index()
@Column('varchar', {
length: 128, array: true, default: '{}',
})
public on: (typeof webhookEventTypes)[number][];
@Column('varchar', {
length: 1024,
})
public url: string;
@Column('varchar', {
length: 1024,
})
public secret: string;
@Index()
@Column('boolean', {
default: true,
})
public active: boolean;
/**
* 直近のリクエスト送信日時
*/
@Column('timestamp with time zone', {
nullable: true,
})
public latestSentAt: Date | null;
/**
* 直近のリクエスト送信時のHTTPステータスコード
*/
@Column('integer', {
nullable: true,
})
public latestStatus: number | null;
}

View File

@ -1,6 +1,4 @@
import { } from 'typeorm';
import { db } from '@/db/postgre.js';
import { getRepository, getCustomRepository } from 'typeorm';
import { Announcement } from './entities/announcement.js';
import { AnnouncementRead } from './entities/announcement-read.js';
import { Instance } from './entities/instance.js';
@ -64,68 +62,66 @@ import { Ad } from './entities/ad.js';
import { PasswordResetRequest } from './entities/password-reset-request.js';
import { UserPending } from './entities/user-pending.js';
import { InstanceRepository } from './repositories/instance.js';
import { Webhook } from './entities/webhook.js';
export const Announcements = db.getRepository(Announcement);
export const AnnouncementReads = db.getRepository(AnnouncementRead);
export const Apps = (AppRepository);
export const Notes = (NoteRepository);
export const NoteFavorites = (NoteFavoriteRepository);
export const NoteWatchings = db.getRepository(NoteWatching);
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
export const NoteReactions = (NoteReactionRepository);
export const NoteUnreads = db.getRepository(NoteUnread);
export const Polls = db.getRepository(Poll);
export const PollVotes = db.getRepository(PollVote);
export const Users = (UserRepository);
export const UserProfiles = db.getRepository(UserProfile);
export const UserKeypairs = db.getRepository(UserKeypair);
export const UserPendings = db.getRepository(UserPending);
export const AttestationChallenges = db.getRepository(AttestationChallenge);
export const UserSecurityKeys = db.getRepository(UserSecurityKey);
export const UserPublickeys = db.getRepository(UserPublickey);
export const UserLists = (UserListRepository);
export const UserListJoinings = db.getRepository(UserListJoining);
export const UserGroups = (UserGroupRepository);
export const UserGroupJoinings = db.getRepository(UserGroupJoining);
export const UserGroupInvitations = (UserGroupInvitationRepository);
export const UserNotePinings = db.getRepository(UserNotePining);
export const UsedUsernames = db.getRepository(UsedUsername);
export const Followings = (FollowingRepository);
export const FollowRequests = (FollowRequestRepository);
export const Instances = (InstanceRepository);
export const Emojis = (EmojiRepository);
export const DriveFiles = (DriveFileRepository);
export const DriveFolders = (DriveFolderRepository);
export const Notifications = (NotificationRepository);
export const Metas = db.getRepository(Meta);
export const Mutings = (MutingRepository);
export const Blockings = (BlockingRepository);
export const SwSubscriptions = db.getRepository(SwSubscription);
export const Hashtags = (HashtagRepository);
export const AbuseUserReports = (AbuseUserReportRepository);
export const RegistrationTickets = db.getRepository(RegistrationTicket);
export const AuthSessions = (AuthSessionRepository);
export const AccessTokens = db.getRepository(AccessToken);
export const Signins = (SigninRepository);
export const MessagingMessages = (MessagingMessageRepository);
export const Pages = (PageRepository);
export const PageLikes = (PageLikeRepository);
export const GalleryPosts = (GalleryPostRepository);
export const GalleryLikes = (GalleryLikeRepository);
export const ModerationLogs = (ModerationLogRepository);
export const Clips = (ClipRepository);
export const ClipNotes = db.getRepository(ClipNote);
export const Antennas = (AntennaRepository);
export const AntennaNotes = db.getRepository(AntennaNote);
export const PromoNotes = db.getRepository(PromoNote);
export const PromoReads = db.getRepository(PromoRead);
export const Relays = (RelayRepository);
export const MutedNotes = db.getRepository(MutedNote);
export const Channels = (ChannelRepository);
export const ChannelFollowings = db.getRepository(ChannelFollowing);
export const ChannelNotePinings = db.getRepository(ChannelNotePining);
export const RegistryItems = db.getRepository(RegistryItem);
export const Webhooks = db.getRepository(Webhook);
export const Ads = db.getRepository(Ad);
export const PasswordResetRequests = db.getRepository(PasswordResetRequest);
export const Announcements = getRepository(Announcement);
export const AnnouncementReads = getRepository(AnnouncementRead);
export const Apps = getCustomRepository(AppRepository);
export const Notes = getCustomRepository(NoteRepository);
export const NoteFavorites = getCustomRepository(NoteFavoriteRepository);
export const NoteWatchings = getRepository(NoteWatching);
export const NoteThreadMutings = getRepository(NoteThreadMuting);
export const NoteReactions = getCustomRepository(NoteReactionRepository);
export const NoteUnreads = getRepository(NoteUnread);
export const Polls = getRepository(Poll);
export const PollVotes = getRepository(PollVote);
export const Users = getCustomRepository(UserRepository);
export const UserProfiles = getRepository(UserProfile);
export const UserKeypairs = getRepository(UserKeypair);
export const UserPendings = getRepository(UserPending);
export const AttestationChallenges = getRepository(AttestationChallenge);
export const UserSecurityKeys = getRepository(UserSecurityKey);
export const UserPublickeys = getRepository(UserPublickey);
export const UserLists = getCustomRepository(UserListRepository);
export const UserListJoinings = getRepository(UserListJoining);
export const UserGroups = getCustomRepository(UserGroupRepository);
export const UserGroupJoinings = getRepository(UserGroupJoining);
export const UserGroupInvitations = getCustomRepository(UserGroupInvitationRepository);
export const UserNotePinings = getRepository(UserNotePining);
export const UsedUsernames = getRepository(UsedUsername);
export const Followings = getCustomRepository(FollowingRepository);
export const FollowRequests = getCustomRepository(FollowRequestRepository);
export const Instances = getCustomRepository(InstanceRepository);
export const Emojis = getCustomRepository(EmojiRepository);
export const DriveFiles = getCustomRepository(DriveFileRepository);
export const DriveFolders = getCustomRepository(DriveFolderRepository);
export const Notifications = getCustomRepository(NotificationRepository);
export const Metas = getRepository(Meta);
export const Mutings = getCustomRepository(MutingRepository);
export const Blockings = getCustomRepository(BlockingRepository);
export const SwSubscriptions = getRepository(SwSubscription);
export const Hashtags = getCustomRepository(HashtagRepository);
export const AbuseUserReports = getCustomRepository(AbuseUserReportRepository);
export const RegistrationTickets = getRepository(RegistrationTicket);
export const AuthSessions = getCustomRepository(AuthSessionRepository);
export const AccessTokens = getRepository(AccessToken);
export const Signins = getCustomRepository(SigninRepository);
export const MessagingMessages = getCustomRepository(MessagingMessageRepository);
export const Pages = getCustomRepository(PageRepository);
export const PageLikes = getCustomRepository(PageLikeRepository);
export const GalleryPosts = getCustomRepository(GalleryPostRepository);
export const GalleryLikes = getCustomRepository(GalleryLikeRepository);
export const ModerationLogs = getCustomRepository(ModerationLogRepository);
export const Clips = getCustomRepository(ClipRepository);
export const ClipNotes = getRepository(ClipNote);
export const Antennas = getCustomRepository(AntennaRepository);
export const AntennaNotes = getRepository(AntennaNote);
export const PromoNotes = getRepository(PromoNote);
export const PromoReads = getRepository(PromoRead);
export const Relays = getCustomRepository(RelayRepository);
export const MutedNotes = getRepository(MutedNote);
export const Channels = getCustomRepository(ChannelRepository);
export const ChannelFollowings = getRepository(ChannelFollowing);
export const ChannelNotePinings = getRepository(ChannelNotePining);
export const RegistryItems = getRepository(RegistryItem);
export const Ads = getRepository(Ad);
export const PasswordResetRequests = getRepository(PasswordResetRequest);

View File

@ -1,13 +1,14 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '../index.js';
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
import { awaitAll } from '@/prelude/await-all.js';
export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).extend({
async pack(
@EntityRepository(AbuseUserReport)
export class AbuseUserReportRepository extends Repository<AbuseUserReport> {
public async pack(
src: AbuseUserReport['id'] | AbuseUserReport,
) {
const report = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const report = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: report.id,
@ -28,11 +29,11 @@ export const AbuseUserReportRepository = db.getRepository(AbuseUserReport).exten
}) : null,
forwarded: report.forwarded,
});
},
}
packMany(
public packMany(
reports: any[],
) {
return Promise.all(reports.map(x => this.pack(x)));
},
});
}
}

View File

@ -1,16 +1,17 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Antenna } from '@/models/entities/antenna.js';
import { Packed } from '@/misc/schema.js';
import { AntennaNotes, UserGroupJoinings } from '../index.js';
export const AntennaRepository = db.getRepository(Antenna).extend({
async pack(
@EntityRepository(Antenna)
export class AntennaRepository extends Repository<Antenna> {
public async pack(
src: Antenna['id'] | Antenna,
): Promise<Packed<'Antenna'>> {
const antenna = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const antenna = typeof src === 'object' ? src : await this.findOneOrFail(src);
const hasUnreadNote = (await AntennaNotes.findOneBy({ antennaId: antenna.id, read: false })) != null;
const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOneBy({ id: antenna.userGroupJoiningId }) : null;
const hasUnreadNote = (await AntennaNotes.findOne({ antennaId: antenna.id, read: false })) != null;
const userGroupJoining = antenna.userGroupJoiningId ? await UserGroupJoinings.findOne(antenna.userGroupJoiningId) : null;
return {
id: antenna.id,
@ -28,5 +29,5 @@ export const AntennaRepository = db.getRepository(Antenna).extend({
withFile: antenna.withFile,
hasUnreadNote,
};
},
});
}
}

View File

@ -1,11 +1,12 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { App } from '@/models/entities/app.js';
import { AccessTokens } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { User } from '../entities/user.js';
export const AppRepository = db.getRepository(App).extend({
async pack(
@EntityRepository(App)
export class AppRepository extends Repository<App> {
public async pack(
src: App['id'] | App,
me?: { id: User['id'] } | null | undefined,
options?: {
@ -20,7 +21,7 @@ export const AppRepository = db.getRepository(App).extend({
includeProfileImageIds: false,
}, options);
const app = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const app = typeof src === 'object' ? src : await this.findOneOrFail(src);
return {
id: app.id,
@ -29,11 +30,11 @@ export const AppRepository = db.getRepository(App).extend({
permission: app.permission,
...(opts.includeSecret ? { secret: app.secret } : {}),
...(me ? {
isAuthorized: await AccessTokens.countBy({
isAuthorized: await AccessTokens.count({
appId: app.id,
userId: me.id,
}).then(count => count > 0),
} : {}),
};
},
});
}
}

View File

@ -1,20 +1,21 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Apps } from '../index.js';
import { AuthSession } from '@/models/entities/auth-session.js';
import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js';
export const AuthSessionRepository = db.getRepository(AuthSession).extend({
async pack(
@EntityRepository(AuthSession)
export class AuthSessionRepository extends Repository<AuthSession> {
public async pack(
src: AuthSession['id'] | AuthSession,
me?: { id: User['id'] } | null | undefined
) {
const session = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const session = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: session.id,
app: Apps.pack(session.appId, me),
token: session.token,
});
},
});
}
}

View File

@ -1,16 +1,17 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '../index.js';
import { Blocking } from '@/models/entities/blocking.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
export const BlockingRepository = db.getRepository(Blocking).extend({
async pack(
@EntityRepository(Blocking)
export class BlockingRepository extends Repository<Blocking> {
public async pack(
src: Blocking['id'] | Blocking,
me?: { id: User['id'] } | null | undefined
): Promise<Packed<'Blocking'>> {
const blocking = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const blocking = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: blocking.id,
@ -20,12 +21,12 @@ export const BlockingRepository = db.getRepository(Blocking).extend({
detail: true,
}),
});
},
}
packMany(
public packMany(
blockings: any[],
me: { id: User['id'] }
) {
return Promise.all(blockings.map(x => this.pack(x, me)));
},
});
}
}

View File

@ -1,22 +1,23 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Channel } from '@/models/entities/channel.js';
import { Packed } from '@/misc/schema.js';
import { DriveFiles, ChannelFollowings, NoteUnreads } from '../index.js';
import { User } from '@/models/entities/user.js';
export const ChannelRepository = db.getRepository(Channel).extend({
async pack(
@EntityRepository(Channel)
export class ChannelRepository extends Repository<Channel> {
public async pack(
src: Channel['id'] | Channel,
me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'Channel'>> {
const channel = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const channel = typeof src === 'object' ? src : await this.findOneOrFail(src);
const meId = me ? me.id : null;
const banner = channel.bannerId ? await DriveFiles.findOneBy({ id: channel.bannerId }) : null;
const banner = channel.bannerId ? await DriveFiles.findOne(channel.bannerId) : null;
const hasUnreadNote = meId ? (await NoteUnreads.findOneBy({ noteChannelId: channel.id, userId: meId })) != null : undefined;
const hasUnreadNote = meId ? (await NoteUnreads.findOne({ noteChannelId: channel.id, userId: meId })) != null : undefined;
const following = meId ? await ChannelFollowings.findOneBy({
const following = meId ? await ChannelFollowings.findOne({
followerId: meId,
followeeId: channel.id,
}) : null;
@ -37,5 +38,5 @@ export const ChannelRepository = db.getRepository(Channel).extend({
hasUnreadNote,
} : {}),
};
},
});
}
}

View File

@ -1,14 +1,15 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Clip } from '@/models/entities/clip.js';
import { Packed } from '@/misc/schema.js';
import { Users } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js';
export const ClipRepository = db.getRepository(Clip).extend({
async pack(
@EntityRepository(Clip)
export class ClipRepository extends Repository<Clip> {
public async pack(
src: Clip['id'] | Clip,
): Promise<Packed<'Clip'>> {
const clip = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const clip = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: clip.id,
@ -19,12 +20,12 @@ export const ClipRepository = db.getRepository(Clip).extend({
description: clip.description,
isPublic: clip.isPublic,
});
},
}
packMany(
public packMany(
clips: Clip[],
) {
return Promise.all(clips.map(x => this.pack(x)));
},
});
}
}

View File

@ -1,4 +1,4 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { DriveFile } from '@/models/entities/drive-file.js';
import { Users, DriveFolders } from '../index.js';
import { User } from '@/models/entities/user.js';
@ -16,8 +16,9 @@ type PackOptions = {
withUser?: boolean,
};
export const DriveFileRepository = db.getRepository(DriveFile).extend({
validateFileName(name: string): boolean {
@EntityRepository(DriveFile)
export class DriveFileRepository extends Repository<DriveFile> {
public validateFileName(name: string): boolean {
return (
(name.trim().length > 0) &&
(name.length <= 200) &&
@ -25,9 +26,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
(name.indexOf('/') === -1) &&
(name.indexOf('..') === -1)
);
},
}
getPublicProperties(file: DriveFile): DriveFile['properties'] {
public getPublicProperties(file: DriveFile): DriveFile['properties'] {
if (file.properties.orientation != null) {
const properties = JSON.parse(JSON.stringify(file.properties));
if (file.properties.orientation >= 5) {
@ -38,9 +39,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
}
return file.properties;
},
}
getPublicUrl(file: DriveFile, thumbnail = false): string | null {
public getPublicUrl(file: DriveFile, thumbnail = false): string | null {
// リモートかつメディアプロキシ
if (file.uri != null && file.userHost != null && config.mediaProxy != null) {
return appendQuery(config.mediaProxy, query({
@ -61,9 +62,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type);
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
},
}
async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
public async calcDriveUsageOf(user: User['id'] | { id: User['id'] }): Promise<number> {
const id = typeof user === 'object' ? user.id : user;
const { sum } = await this
@ -74,9 +75,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
.getRawOne();
return parseInt(sum, 10) || 0;
},
}
async calcDriveUsageOfHost(host: string): Promise<number> {
public async calcDriveUsageOfHost(host: string): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost = :host', { host: toPuny(host) })
@ -85,9 +86,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
.getRawOne();
return parseInt(sum, 10) || 0;
},
}
async calcDriveUsageOfLocal(): Promise<number> {
public async calcDriveUsageOfLocal(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NULL')
@ -96,9 +97,9 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
.getRawOne();
return parseInt(sum, 10) || 0;
},
}
async calcDriveUsageOfRemote(): Promise<number> {
public async calcDriveUsageOfRemote(): Promise<number> {
const { sum } = await this
.createQueryBuilder('file')
.where('file.userHost IS NOT NULL')
@ -107,9 +108,11 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
.getRawOne();
return parseInt(sum, 10) || 0;
},
}
async pack(
public async pack(src: DriveFile['id'], options?: PackOptions): Promise<Packed<'DriveFile'> | null>;
public async pack(src: DriveFile, options?: PackOptions): Promise<Packed<'DriveFile'>>;
public async pack(
src: DriveFile['id'] | DriveFile,
options?: PackOptions
): Promise<Packed<'DriveFile'> | null> {
@ -118,9 +121,11 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
self: false,
}, options);
const file = typeof src === 'object' ? src : await this.findOneBy({ id: src });
const file = typeof src === 'object' ? src : await this.findOne(src);
if (file == null) return null;
const meta = await fetchMeta();
return await awaitAll<Packed<'DriveFile'>>({
id: file.id,
createdAt: file.createdAt.toISOString(),
@ -141,13 +146,13 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
userId: opts.withUser ? file.userId : null,
user: (opts.withUser && file.userId) ? Users.pack(file.userId) : null,
});
},
}
async packMany(
public async packMany(
files: (DriveFile['id'] | DriveFile)[],
options?: PackOptions
) {
const items = await Promise.all(files.map(f => this.pack(f, options)));
return items.filter(x => x != null);
},
});
}
}

View File

@ -1,11 +1,12 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { DriveFolders, DriveFiles } from '../index.js';
import { DriveFolder } from '@/models/entities/drive-folder.js';
import { awaitAll } from '@/prelude/await-all.js';
import { Packed } from '@/misc/schema.js';
export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
async pack(
@EntityRepository(DriveFolder)
export class DriveFolderRepository extends Repository<DriveFolder> {
public async pack(
src: DriveFolder['id'] | DriveFolder,
options?: {
detail: boolean
@ -15,7 +16,7 @@ export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
detail: false,
}, options);
const folder = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const folder = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: folder.id,
@ -24,10 +25,10 @@ export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
parentId: folder.parentId,
...(opts.detail ? {
foldersCount: DriveFolders.countBy({
foldersCount: DriveFolders.count({
parentId: folder.id,
}),
filesCount: DriveFiles.countBy({
filesCount: DriveFiles.count({
folderId: folder.id,
}),
@ -38,5 +39,5 @@ export const DriveFolderRepository = db.getRepository(DriveFolder).extend({
} : {}),
} : {}),
});
},
});
}
}

View File

@ -1,12 +1,13 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Emoji } from '@/models/entities/emoji.js';
import { Packed } from '@/misc/schema.js';
export const EmojiRepository = db.getRepository(Emoji).extend({
async pack(
@EntityRepository(Emoji)
export class EmojiRepository extends Repository<Emoji> {
public async pack(
src: Emoji['id'] | Emoji,
): Promise<Packed<'Emoji'>> {
const emoji = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const emoji = typeof src === 'object' ? src : await this.findOneOrFail(src);
return {
id: emoji.id,
@ -17,11 +18,11 @@ export const EmojiRepository = db.getRepository(Emoji).extend({
// || emoji.originalUrl してるのは後方互換性のため
url: emoji.publicUrl || emoji.originalUrl,
};
},
}
packMany(
public packMany(
emojis: any[],
) {
return Promise.all(emojis.map(x => this.pack(x)));
},
});
}
}

View File

@ -1,19 +1,20 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { FollowRequest } from '@/models/entities/follow-request.js';
import { Users } from '../index.js';
import { User } from '@/models/entities/user.js';
export const FollowRequestRepository = db.getRepository(FollowRequest).extend({
async pack(
@EntityRepository(FollowRequest)
export class FollowRequestRepository extends Repository<FollowRequest> {
public async pack(
src: FollowRequest['id'] | FollowRequest,
me?: { id: User['id'] } | null | undefined
) {
const request = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const request = typeof src === 'object' ? src : await this.findOneOrFail(src);
return {
id: request.id,
follower: await Users.pack(request.followerId, me),
followee: await Users.pack(request.followeeId, me),
};
},
});
}
}

View File

@ -1,4 +1,4 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '../index.js';
import { Following } from '@/models/entities/following.js';
import { awaitAll } from '@/prelude/await-all.js';
@ -29,24 +29,25 @@ type RemoteFolloweeFollowing = Following & {
followeeSharedInbox: string;
};
export const FollowingRepository = db.getRepository(Following).extend({
isLocalFollower(following: Following): following is LocalFollowerFollowing {
@EntityRepository(Following)
export class FollowingRepository extends Repository<Following> {
public isLocalFollower(following: Following): following is LocalFollowerFollowing {
return following.followerHost == null;
},
}
isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
public isRemoteFollower(following: Following): following is RemoteFollowerFollowing {
return following.followerHost != null;
},
}
isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
public isLocalFollowee(following: Following): following is LocalFolloweeFollowing {
return following.followeeHost == null;
},
}
isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
public isRemoteFollowee(following: Following): following is RemoteFolloweeFollowing {
return following.followeeHost != null;
},
}
async pack(
public async pack(
src: Following['id'] | Following,
me?: { id: User['id'] } | null | undefined,
opts?: {
@ -54,7 +55,7 @@ export const FollowingRepository = db.getRepository(Following).extend({
populateFollower?: boolean;
}
): Promise<Packed<'Following'>> {
const following = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const following = typeof src === 'object' ? src : await this.findOneOrFail(src);
if (opts == null) opts = {};
@ -70,9 +71,9 @@ export const FollowingRepository = db.getRepository(Following).extend({
detail: true,
}) : undefined,
});
},
}
packMany(
public packMany(
followings: any[],
me?: { id: User['id'] } | null | undefined,
opts?: {
@ -81,5 +82,5 @@ export const FollowingRepository = db.getRepository(Following).extend({
}
) {
return Promise.all(followings.map(x => this.pack(x, me, opts)));
},
});
}
}

View File

@ -1,24 +1,25 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { GalleryLike } from '@/models/entities/gallery-like.js';
import { GalleryPosts } from '../index.js';
export const GalleryLikeRepository = db.getRepository(GalleryLike).extend({
async pack(
@EntityRepository(GalleryLike)
export class GalleryLikeRepository extends Repository<GalleryLike> {
public async pack(
src: GalleryLike['id'] | GalleryLike,
me?: any
) {
const like = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const like = typeof src === 'object' ? src : await this.findOneOrFail(src);
return {
id: like.id,
post: await GalleryPosts.pack(like.post || like.postId, me),
};
},
}
packMany(
public packMany(
likes: any[],
me: any
) {
return Promise.all(likes.map(x => this.pack(x, me)));
},
});
}
}

View File

@ -1,17 +1,18 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { GalleryPost } from '@/models/entities/gallery-post.js';
import { Packed } from '@/misc/schema.js';
import { Users, DriveFiles, GalleryLikes } from '../index.js';
import { awaitAll } from '@/prelude/await-all.js';
import { User } from '@/models/entities/user.js';
export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
async pack(
@EntityRepository(GalleryPost)
export class GalleryPostRepository extends Repository<GalleryPost> {
public async pack(
src: GalleryPost['id'] | GalleryPost,
me?: { id: User['id'] } | null | undefined,
): Promise<Packed<'GalleryPost'>> {
const meId = me ? me.id : null;
const post = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const post = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: post.id,
@ -26,14 +27,14 @@ export const GalleryPostRepository = db.getRepository(GalleryPost).extend({
tags: post.tags.length > 0 ? post.tags : undefined,
isSensitive: post.isSensitive,
likedCount: post.likedCount,
isLiked: meId ? await GalleryLikes.findOneBy({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
isLiked: meId ? await GalleryLikes.findOne({ postId: post.id, userId: meId }).then(x => x != null) : undefined,
});
},
}
packMany(
public packMany(
posts: GalleryPost[],
me?: { id: User['id'] } | null | undefined,
) {
return Promise.all(posts.map(x => this.pack(x, me)));
},
});
}
}

View File

@ -1,9 +1,10 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Hashtag } from '@/models/entities/hashtag.js';
import { Packed } from '@/misc/schema.js';
export const HashtagRepository = db.getRepository(Hashtag).extend({
async pack(
@EntityRepository(Hashtag)
export class HashtagRepository extends Repository<Hashtag> {
public async pack(
src: Hashtag,
): Promise<Packed<'Hashtag'>> {
return {
@ -15,11 +16,11 @@ export const HashtagRepository = db.getRepository(Hashtag).extend({
attachedLocalUsersCount: src.attachedLocalUsersCount,
attachedRemoteUsersCount: src.attachedRemoteUsersCount,
};
},
}
packMany(
public packMany(
hashtags: Hashtag[],
) {
return Promise.all(hashtags.map(x => this.pack(x)));
},
});
}
}

View File

@ -1,9 +1,10 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Instance } from '@/models/entities/instance.js';
import { Packed } from '@/misc/schema.js';
export const InstanceRepository = db.getRepository(Instance).extend({
async pack(
@EntityRepository(Instance)
export class InstanceRepository extends Repository<Instance> {
public async pack(
instance: Instance,
): Promise<Packed<'FederationInstance'>> {
return {
@ -28,11 +29,11 @@ export const InstanceRepository = db.getRepository(Instance).extend({
iconUrl: instance.iconUrl,
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
};
},
}
packMany(
public packMany(
instances: Instance[],
) {
return Promise.all(instances.map(x => this.pack(x)));
},
});
}
}

View File

@ -1,11 +1,12 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { MessagingMessage } from '@/models/entities/messaging-message.js';
import { Users, DriveFiles, UserGroups } from '../index.js';
import { Packed } from '@/misc/schema.js';
import { User } from '@/models/entities/user.js';
export const MessagingMessageRepository = db.getRepository(MessagingMessage).extend({
async pack(
@EntityRepository(MessagingMessage)
export class MessagingMessageRepository extends Repository<MessagingMessage> {
public async pack(
src: MessagingMessage['id'] | MessagingMessage,
me?: { id: User['id'] } | null | undefined,
options?: {
@ -18,7 +19,7 @@ export const MessagingMessageRepository = db.getRepository(MessagingMessage).ext
populateGroup: true,
};
const message = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const message = typeof src === 'object' ? src : await this.findOneOrFail(src);
return {
id: message.id,
@ -35,5 +36,5 @@ export const MessagingMessageRepository = db.getRepository(MessagingMessage).ext
isRead: message.isRead,
reads: message.reads,
};
},
});
}
}

View File

@ -1,13 +1,14 @@
import { db } from '@/db/postgre.js';
import { EntityRepository, Repository } from 'typeorm';
import { Users } from '../index.js';
import { ModerationLog } from '@/models/entities/moderation-log.js';
import { awaitAll } from '@/prelude/await-all.js';
export const ModerationLogRepository = db.getRepository(ModerationLog).extend({
async pack(
@EntityRepository(ModerationLog)
export class ModerationLogRepository extends Repository<ModerationLog> {
public async pack(
src: ModerationLog['id'] | ModerationLog,
) {
const log = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
const log = typeof src === 'object' ? src : await this.findOneOrFail(src);
return await awaitAll({
id: log.id,
@ -19,11 +20,11 @@ export const ModerationLogRepository = db.getRepository(ModerationLog).extend({
detail: true,
}),
});
},
}
packMany(
public packMany(
reports: any[],
) {
return Promise.all(reports.map(x => this.pack(x)));
},
});
}
}

Some files were not shown because too many files have changed in this diff Show More