Compare commits
200 Commits
12.109.1-s
...
ncat/dev/e
Author | SHA1 | Date | |
---|---|---|---|
ffe75d85ae | |||
8168b4edad | |||
ab9b648c59 | |||
3658f19d98 | |||
e213c2e844 | |||
dd86397e85 | |||
6d33b366f8 | |||
710e633e33 | |||
dfe6615d68 | |||
33c22b5f3e | |||
16c7ef41fb | |||
6e50579f9f | |||
08ff4926c7 | |||
a7015e6f09 | |||
fd4ec81bcb | |||
9c33e6eef7 | |||
9f7cdb4bc7 | |||
a9ec9df606 | |||
daa0ca72a7 | |||
67fc39b8db | |||
45c457b8b3 | |||
2ff8ace195 | |||
17081c4612 | |||
deaea4a0d8 | |||
8646f653d0 | |||
8252edfc1e | |||
cf77eaaba9 | |||
d52b8ca8ab | |||
0109308f5a | |||
fc06bb8c49 | |||
ec3be7e4d3 | |||
f8e6f3cc73 | |||
fc39383d65 | |||
7f5d189528 | |||
ff9a074ab6 | |||
91f4ec3747 | |||
41c2aed7dc | |||
b8360313e8 | |||
403b82277c | |||
433505df48 | |||
090f8eff67 | |||
395fe7eb4b | |||
c8935b32f8 | |||
ebb687cde4 | |||
e47a8bf666 | |||
408d54f2eb | |||
f9b5d92176 | |||
61dfa6d598 | |||
2f621ceb7a | |||
483c97e224 | |||
b6a92f3680 | |||
a1a6e3560a | |||
c7f9a84970 | |||
81374e0eac | |||
7cf524c8f8 | |||
1c1013046e | |||
2689836ab1 | |||
cfc5578d14 | |||
c4a166db59 | |||
22933ef7f1 | |||
692a1ba551 | |||
9507b44d22 | |||
f7114628e0 | |||
9a53bb67fc | |||
18a2d430fc | |||
4a836121b4 | |||
30399dee8f | |||
f0f3442fb1 | |||
d31446d8aa | |||
c214fe17bf | |||
5f40394a8e | |||
97f725ae9c | |||
0706c775ac | |||
be45be0be9 | |||
9abbabd64f | |||
39221998bd | |||
de66f35798 | |||
2b402fe3a0 | |||
03fe23d507 | |||
b2ffcb77e0 | |||
f37bd9ea65 | |||
750d528db2 | |||
d837bbf5f7 | |||
38d3703484 | |||
7667493d56 | |||
ff96126057 | |||
477163cb84 | |||
a3dbde894a | |||
59b2715058 | |||
270b842c71 | |||
8b197ed4d6 | |||
3a38bbbe6d | |||
f70838a983 | |||
1fd2a7ec13 | |||
dac56bace9 | |||
bb655b1807 | |||
b68e98d521 | |||
c36dee8808 | |||
86847f1866 | |||
203454848a | |||
2f640ccdf1 | |||
aaf0f9aab1 | |||
377f84e26e | |||
7384c55b69 | |||
83726b9c96 | |||
3295696a20 | |||
054db12d02 | |||
3de91eee2c | |||
3ef1748e6d | |||
8402a9b665 | |||
dd47d50679 | |||
595f01ed28 | |||
3a83aabf42 | |||
87ba0ec566 | |||
75482bb4b3 | |||
c57f03b092 | |||
e53e40bf62 | |||
be4d3a2b84 | |||
08d0b0f5ef | |||
828121217d | |||
49fad60683 | |||
adc85b8af4 | |||
7532e44d37 | |||
081a312132 | |||
f5ba0db663 | |||
ed727aac78 | |||
8f85675275 | |||
5d1946465b | |||
839dbd98da | |||
866f3954a9 | |||
dd2f324050 | |||
ee6209afb2 | |||
3b68630e61 | |||
8f539adfdb | |||
56f552cc89 | |||
bb7fc84fab | |||
e7ae677b71 | |||
64f80f122f | |||
43a59c9b80 | |||
53f548bb08 | |||
291743087d | |||
432210e1b5 | |||
cb115d31cc | |||
088ecac89d | |||
7257336dc5 | |||
feb4373735 | |||
9861135546 | |||
22c0d5cb4e | |||
9d1e4b9014 | |||
99971cb91c | |||
c40da925a3 | |||
7c75e6666e | |||
922b7b0ad3 | |||
05c2db1dc0 | |||
282c2be5af | |||
6fd089d31a | |||
4850919a5b | |||
ed0eafbfd0 | |||
89120d45c0 | |||
1eedeb5842 | |||
c363154e07 | |||
87627eebaa | |||
a5956fc2f4 | |||
2924b84deb | |||
3b303a20a4 | |||
0fb9a438f9 | |||
139f4ae26b | |||
7e25be2513 | |||
c32edde497 | |||
b6e66d1d39 | |||
1d3ec1f31c | |||
a8abf35a91 | |||
90462ea1de | |||
bb1d02bde2 | |||
884f6a2fc9 | |||
d5ae48a754 | |||
0e04fd26d7 | |||
14440a95af | |||
645d945a33 | |||
1024fa4706 | |||
f231ad84e9 | |||
5c79e72774 | |||
639024897a | |||
0103cf8cd5 | |||
37129c2aa2 | |||
30f7e9e104 | |||
edad7fdbfe | |||
ec6facfe7d | |||
42184984a2 | |||
cc82bc182a | |||
6e52dc622d | |||
55ee63a57e | |||
a03f330c49 | |||
01185a830b | |||
502441c0e8 | |||
e1985690d2 | |||
eedd182187 | |||
e9e803db18 | |||
22c91811ad | |||
ded13933f2 |
18
.github/workflows/docker-image.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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)
|
12
.github/workflows/lint.yml
vendored
@ -14,12 +14,12 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 12.x
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
node-version: 16.x
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: |
|
||||
packages/backend/yarn.lock
|
||||
packages/client/yarn.lock
|
||||
- run: yarn install
|
||||
- run: yarn lint
|
||||
|
84
.github/workflows/test.yml
vendored
@ -8,44 +8,48 @@ on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
#mocha:
|
||||
# runs-on: ubuntu-latest
|
||||
mocha:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# strategy:
|
||||
# matrix:
|
||||
# node-version: [16.x]
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x]
|
||||
|
||||
# services:
|
||||
# postgres:
|
||||
# image: postgres:13
|
||||
# ports:
|
||||
# - 54312:5432
|
||||
# env:
|
||||
# POSTGRES_DB: test-misskey
|
||||
# POSTGRES_HOST_AUTH_METHOD: trust
|
||||
# redis:
|
||||
# image: redis:6
|
||||
# ports:
|
||||
# - 56312:6379
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:13
|
||||
ports:
|
||||
- 54312:5432
|
||||
env:
|
||||
POSTGRES_DB: test-misskey
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
redis:
|
||||
image: redis:6
|
||||
ports:
|
||||
- 56312:6379
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# with:
|
||||
# submodules: true
|
||||
# - name: Use Node.js ${{ matrix.node-version }}
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: ${{ matrix.node-version }}
|
||||
# - name: Install dependencies
|
||||
# run: yarn install
|
||||
# - name: Check yarn.lock
|
||||
# run: git diff --exit-code yarn.lock
|
||||
# - name: Copy Configure
|
||||
# run: cp .github/misskey/test.yml .config
|
||||
# - name: Build
|
||||
# run: yarn build
|
||||
# - name: Test
|
||||
# run: yarn mocha
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
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
|
||||
run: git diff --exit-code yarn.lock
|
||||
- name: Copy Configure
|
||||
run: cp .github/misskey/test.yml .config
|
||||
- name: Build
|
||||
run: yarn build
|
||||
- name: Test
|
||||
run: yarn mocha
|
||||
|
||||
e2e:
|
||||
runs-on: ubuntu-latest
|
||||
@ -80,13 +84,13 @@ jobs:
|
||||
#- uses: browser-actions/setup-firefox@latest
|
||||
# if: ${{ matrix.browser == 'firefox' }}
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: '**/node_modules'
|
||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: |
|
||||
packages/backend/yarn.lock
|
||||
packages/client/yarn.lock
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
- name: Check yarn.lock
|
||||
|
2
.gitignore
vendored
@ -42,5 +42,3 @@ ormconfig.json
|
||||
*.blend3
|
||||
*.blend4
|
||||
*.blend5
|
||||
|
||||
start.sh
|
22
CHANGELOG.md
@ -10,6 +10,28 @@
|
||||
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
|
||||
|
@ -32,4 +32,3 @@ COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node
|
||||
COPY . ./
|
||||
|
||||
CMD ["npm", "run", "migrateandstart"]
|
||||
|
||||
|
173
README.md
@ -1,168 +1,7 @@
|
||||
[](https://join.misskey.page/)
|
||||
## これってなに?
|
||||
[Misskey](https://github.com/misskey-dev/misskey)ベースのSNS
|
||||
|
||||
<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
|
||||
## 本家との違い
|
||||
[ここ](https://nullc.at/nca10.net/)にかいてあるよ
|
||||
## 導入方法
|
||||
[MisskeyHub](https://misskey-hub.net/docs/install.html)をみてね
|
||||
|
Before Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 317 KiB |
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 200 KiB |
BIN
assets/ai.png
Before Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 238 KiB |
Before Width: | Height: | Size: 148 KiB |
BIN
assets/title.png
Before Width: | Height: | Size: 3.8 KiB |
@ -1,52 +1,31 @@
|
||||
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:
|
||||
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:
|
||||
|
6
explanation/instancecolor.md
Normal file
@ -0,0 +1,6 @@
|
||||
`packages/backend/src/server/web/views/base.pug` の中の
|
||||
```
|
||||
meta(name='theme-color' content='#好きな色')
|
||||
meta(name='theme-color-orig' content='#好きな色')
|
||||
```
|
||||
の好きな色の部分を変更する(Hex値)
|
15
explanation/ncatlang.md
Normal file
@ -0,0 +1,15 @@
|
||||
## 主な変更点
|
||||
- なとナがにゃ
|
||||
- コピーがコビー
|
||||
- あなたが僕
|
||||
- パスワードが鍵
|
||||
- doneがドネ
|
||||
- ユーザー名が名前
|
||||
- バナーがバニャーニャ
|
||||
- ユーザーネームがid
|
||||
- コントロールパネルがコンソトールパネル
|
||||
|
||||
## ぬるきゃごの導入方法
|
||||
1. `./locales/ja-JP.yml` を `ja-NCAT.yml` に変更
|
||||
2. `ja-NCAT.yml` を `./locales` のなかに入れる
|
||||
3. `./locales/inde.js` の `const languages` に `ja-NCAT` を追加する
|
@ -908,6 +908,7 @@ _mfm:
|
||||
rainbow: "قوس قزح"
|
||||
rainbowDescription: "اجعل المحتوى يظهر بألوان الطيف"
|
||||
rotate: "تدوير"
|
||||
rotateDescription: "يُدير المحتوى بزاوية معيّنة."
|
||||
_instanceTicker:
|
||||
none: "لا تظهره بتاتًا"
|
||||
remote: "أظهر للمستخدمين البِعاد"
|
||||
@ -1236,6 +1237,8 @@ _pages:
|
||||
font: "الخط"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
eyeCatchingImageSet: "عيّن صورة مصغّرة"
|
||||
eyeCatchingImageRemove: "احذف صورة مصغّرة"
|
||||
chooseBlock: "إضافة كتلة"
|
||||
selectType: "اختر النوع"
|
||||
enterVariableName: "أدخل اسم المتغيّر"
|
||||
@ -1496,6 +1499,7 @@ _notification:
|
||||
pollVote: "مصوِت شارك في الاستطلاع"
|
||||
receiveFollowRequest: "طلبات المتابعة المتلقاة"
|
||||
followRequestAccepted: "طلبات المتابعة المقبولة"
|
||||
groupInvited: "دعوات الفريق"
|
||||
app: "إشعارات التطبيقات المرتبطة"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا"
|
||||
|
@ -832,6 +832,10 @@ size: "আকার"
|
||||
numberOfColumn: "কলামের সংখ্যা"
|
||||
searchByGoogle: "গুগল"
|
||||
indefinitely: "অনির্দিষ্ট"
|
||||
tenMinutes: "১০ মিনিট"
|
||||
oneHour: "১ ঘণ্টা"
|
||||
oneDay: "একদিন"
|
||||
oneWeek: "এক সপ্তাহ"
|
||||
_emailUnavailable:
|
||||
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
|
||||
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
|
||||
|
@ -94,6 +94,8 @@ 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."
|
||||
|
1151
locales/eo-UY.yml
@ -19,12 +19,10 @@ 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',
|
||||
@ -58,7 +56,6 @@ 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(
|
||||
|
1793
locales/ja-NY.yml
@ -481,13 +481,24 @@ 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"
|
||||
@ -522,12 +533,42 @@ removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}.
|
||||
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:
|
||||
@ -538,9 +579,11 @@ _mfm:
|
||||
emoji: "Emoji personalizat"
|
||||
search: "Caută"
|
||||
_theme:
|
||||
description: "Descriere"
|
||||
keys:
|
||||
mention: "Mențiune"
|
||||
renote: "Re-notează"
|
||||
divider: "Separator"
|
||||
_sfx:
|
||||
note: "Note"
|
||||
notification: "Notificări"
|
||||
|
@ -81,6 +81,8 @@ somethingHappened: "發生錯誤"
|
||||
retry: "重試"
|
||||
pageLoadError: "載入頁面失敗"
|
||||
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
|
||||
serverIsDead: "伺服器沒有回應。請稍等片刻,然後重試。"
|
||||
youShouldUpgradeClient: "請重新載入以使用新版本的客戶端顯示此頁面"
|
||||
enterListName: "輸入清單名稱"
|
||||
privacy: "隱私"
|
||||
makeFollowManuallyApprove: "手動審核追隨請求"
|
||||
@ -104,6 +106,7 @@ clickToShow: "按一下以顯示"
|
||||
sensitive: "敏感內容"
|
||||
add: "新增"
|
||||
reaction: "情感"
|
||||
reactionSetting: "在選擇器中顯示反應"
|
||||
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
|
||||
rememberNoteVisibility: "記住貼文可見性"
|
||||
attachCancel: "移除附件"
|
||||
@ -138,6 +141,7 @@ flagAsBot: "此使用者是機器人"
|
||||
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
|
||||
flagAsCat: "此使用者是貓"
|
||||
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
|
||||
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
|
||||
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
|
||||
addAccount: "添加帳戶"
|
||||
loginFailed: "登入失敗"
|
||||
@ -599,6 +603,9 @@ reportAbuse: "檢舉"
|
||||
reportAbuseOf: "檢舉{name}"
|
||||
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話,請附上針對的URL網址。"
|
||||
abuseReported: "回報已送出。感謝您的報告。"
|
||||
reporter: "檢舉者"
|
||||
reporteeOrigin: "檢舉來源"
|
||||
reporterOrigin: "檢舉者來源"
|
||||
send: "發送"
|
||||
abuseMarkAsResolved: "處理完畢"
|
||||
openInNewTab: "在新分頁中開啟"
|
||||
@ -734,6 +741,7 @@ postToGallery: "發佈到相簿"
|
||||
gallery: "相簿"
|
||||
recentPosts: "最新貼文"
|
||||
popularPosts: "熱門的貼文"
|
||||
shareWithNote: "在貼文中分享"
|
||||
ads: "廣告"
|
||||
expiration: "期限"
|
||||
memo: "備忘錄"
|
||||
@ -743,14 +751,35 @@ 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: "降低此廣告的頻率 "
|
||||
|
@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "12.109.1-simkey-v1",
|
||||
"version": "12.110.1-nca10.net-v1",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sim1222/misskey.git"
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@ -41,7 +41,7 @@
|
||||
"js-yaml": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "5.17.0",
|
||||
"@typescript-eslint/parser": "5.18.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "9.5.3",
|
||||
"start-server-and-test": "1.14.0",
|
||||
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 74 KiB |
@ -5,7 +5,7 @@
|
||||
"scripts": {
|
||||
"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"
|
||||
},
|
||||
@ -55,25 +55,24 @@
|
||||
"@types/redis": "4.0.11",
|
||||
"@types/rename": "1.0.4",
|
||||
"@types/sanitize-html": "2.6.2",
|
||||
"@types/sharp": "0.30.0",
|
||||
"@types/sharp": "0.30.1",
|
||||
"@types/sinonjs__fake-timers": "8.1.2",
|
||||
"@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.17.0",
|
||||
"@typescript-eslint/parser": "5.17.0",
|
||||
"@bull-board/koa": "3.10.2",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
"@typescript-eslint/parser": "5.18.0",
|
||||
"@bull-board/koa": "3.10.3",
|
||||
"abort-controller": "3.0.0",
|
||||
"ajv": "8.11.0",
|
||||
"archiver": "5.3.0",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.1105.0",
|
||||
"aws-sdk": "2.1111.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.5",
|
||||
"broadcast-channel": "4.10.0",
|
||||
@ -89,8 +88,8 @@
|
||||
"date-fns": "2.28.0",
|
||||
"deep-email-validator": "0.1.21",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "8.12.0",
|
||||
"eslint-plugin-import": "2.25.4",
|
||||
"eslint": "8.13.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"feed": "4.2.2",
|
||||
"file-type": "17.1.1",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
@ -145,7 +144,7 @@
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"sanitize-html": "2.7.0",
|
||||
"semver": "7.3.5",
|
||||
"semver": "7.3.6",
|
||||
"sharp": "0.30.3",
|
||||
"speakeasy": "2.0.0",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
@ -154,7 +153,6 @@
|
||||
"summaly": "2.5.0",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "5.11.9",
|
||||
"throttle-debounce": "3.0.1",
|
||||
"tinycolor2": "1.4.2",
|
||||
"tmp": "0.2.1",
|
||||
"ts-loader": "9.2.8",
|
||||
@ -162,7 +160,7 @@
|
||||
"tsc-alias": "1.4.1",
|
||||
"tsconfig-paths": "3.14.1",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typeorm": "0.3.4",
|
||||
"typeorm": "0.3.5",
|
||||
"typescript": "4.6.3",
|
||||
"ulid": "2.3.0",
|
||||
"unzipper": "0.10.11",
|
||||
@ -173,7 +171,7 @@
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@redocly/openapi-core": "1.0.0-beta.91",
|
||||
"@redocly/openapi-core": "1.0.0-beta.93",
|
||||
"@types/fluent-ffmpeg": "2.1.20",
|
||||
"cross-env": "7.0.3",
|
||||
"execa": "6.1.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
export const MAX_NOTE_TEXT_LENGTH = 8192;
|
||||
export const MAX_NOTE_TEXT_LENGTH = 3000;
|
||||
|
||||
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
|
||||
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
|
||||
|
@ -209,7 +209,11 @@ export const db = new DataSource({
|
||||
});
|
||||
|
||||
export async function initDb() {
|
||||
await db.connect();
|
||||
if (db.isInitialized) {
|
||||
// nop
|
||||
} else {
|
||||
await db.connect();
|
||||
}
|
||||
}
|
||||
|
||||
export async function resetDb() {
|
||||
|
@ -42,7 +42,8 @@ async function getCaptchaResponse(url: string, secret: string, response: string)
|
||||
headers: {
|
||||
'User-Agent': config.userAgent,
|
||||
},
|
||||
timeout: 10 * 1000,
|
||||
// TODO
|
||||
//timeout: 10 * 1000,
|
||||
agent: getAgentByUrl,
|
||||
}).catch(e => {
|
||||
throw `${e.message || e}`;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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})`;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
||||
import { DriveFile } from './drive-file.js';
|
||||
import { id } from '../id.js';
|
||||
import { DriveFile } from './drive-file.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;
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { EntityRepository, Repository, In, Not } from 'typeorm';
|
||||
import Ajv from 'ajv';
|
||||
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
|
||||
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js';
|
||||
import config from '@/config/index.js';
|
||||
import { Packed } from '@/misc/schema.js';
|
||||
import { awaitAll, Promiseable } from '@/prelude/await-all.js';
|
||||
@ -9,8 +8,9 @@ import { populateEmojis } from '@/misc/populate-emojis.js';
|
||||
import { getAntennas } from '@/misc/antenna-cache.js';
|
||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
||||
import { Cache } from '@/misc/cache.js';
|
||||
import { Instance } from '../entities/instance.js';
|
||||
import { db } from '@/db/postgre.js';
|
||||
import { Instance } from '../entities/instance.js';
|
||||
import { Notes, NoteUnreads, FollowRequests, Notifications, MessagingMessages, UserNotePinings, Followings, Blockings, Mutings, UserProfiles, UserSecurityKeys, UserGroupJoinings, Pages, Announcements, AnnouncementReads, Antennas, AntennaNotes, ChannelFollowings, Instances, DriveFiles } from '../index.js';
|
||||
|
||||
const userInstanceCache = new Cache<Instance | null>(1000 * 60 * 60 * 3);
|
||||
|
||||
@ -112,7 +112,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
const joinings = await UserGroupJoinings.findBy({ userId: userId });
|
||||
|
||||
const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
|
||||
.where(`message.groupId = :groupId`, { groupId: j.userGroupId })
|
||||
.where('message.groupId = :groupId', { groupId: j.userGroupId })
|
||||
.andWhere('message.userId != :userId', { userId: userId })
|
||||
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
|
||||
.andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
||||
@ -204,8 +204,18 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
);
|
||||
},
|
||||
|
||||
getAvatarUrl(user: User): string {
|
||||
// TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング
|
||||
async getAvatarUrl(user: User): Promise<string> {
|
||||
if (user.avatar) {
|
||||
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
|
||||
} else if (user.avatarId) {
|
||||
const avatar = await DriveFiles.findOneByOrFail({ id: user.avatarId });
|
||||
return DriveFiles.getPublicUrl(avatar, true) || this.getIdenticonUrl(user.id);
|
||||
} else {
|
||||
return this.getIdenticonUrl(user.id);
|
||||
}
|
||||
},
|
||||
|
||||
getAvatarUrlSync(user: User): string {
|
||||
if (user.avatar) {
|
||||
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
|
||||
} else {
|
||||
@ -223,7 +233,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
options?: {
|
||||
detail?: D,
|
||||
includeSecrets?: boolean,
|
||||
}
|
||||
},
|
||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||
const opts = Object.assign({
|
||||
detail: false,
|
||||
@ -274,7 +284,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: this.getAvatarUrl(user),
|
||||
avatarUrl: this.getAvatarUrlSync(user),
|
||||
avatarBlurhash: user.avatar?.blurhash || null,
|
||||
avatarColor: null, // 後方互換性のため
|
||||
isAdmin: user.isAdmin || falsy,
|
||||
@ -283,7 +293,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
isCat: user.isCat || falsy,
|
||||
instance: user.host ? userInstanceCache.fetch(user.host,
|
||||
() => Instances.findOneBy({ host: user.host! }),
|
||||
v => v != null
|
||||
v => v != null,
|
||||
).then(instance => instance ? {
|
||||
name: instance.name,
|
||||
softwareName: instance.softwareName,
|
||||
@ -403,7 +413,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||
options?: {
|
||||
detail?: D,
|
||||
includeSecrets?: boolean,
|
||||
}
|
||||
},
|
||||
): Promise<IsUserDetailed<D>[]> {
|
||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||
},
|
||||
|
@ -27,6 +27,7 @@ export const packedEmojiSchema = {
|
||||
host: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
|
@ -21,6 +21,7 @@ export const packedUserLiteSchema = {
|
||||
type: 'string',
|
||||
nullable: true, optional: false,
|
||||
example: 'misskey.example.com',
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
avatarUrl: {
|
||||
type: 'string',
|
||||
|
@ -1,4 +1,5 @@
|
||||
import httpSignature from 'http-signature';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import config from '@/config/index.js';
|
||||
import { envOption } from '../env.js';
|
||||
@ -16,7 +17,7 @@ import { getJobInfo } from './get-job-info.js';
|
||||
import { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js';
|
||||
import { ThinUser } from './types.js';
|
||||
import { IActivity } from '@/remote/activitypub/type.js';
|
||||
import { Webhook } from '@/models/entities/webhook.js';
|
||||
import { Webhook, webhookEventTypes } from '@/models/entities/webhook.js';
|
||||
|
||||
function renderError(e: Error): any {
|
||||
return {
|
||||
@ -262,12 +263,16 @@ export function createCleanRemoteFilesJob() {
|
||||
});
|
||||
}
|
||||
|
||||
export function webhookDeliver(webhook: Webhook, content: unknown) {
|
||||
export function webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) {
|
||||
const data = {
|
||||
type,
|
||||
content,
|
||||
webhookId: webhook.id,
|
||||
userId: webhook.userId,
|
||||
to: webhook.url,
|
||||
secret: webhook.secret,
|
||||
createdAt: Date.now(),
|
||||
eventId: uuid(),
|
||||
};
|
||||
|
||||
return webhookDeliverQueue.add(data, {
|
||||
|
@ -8,13 +8,9 @@ import config from '@/config/index.js';
|
||||
|
||||
const logger = new Logger('webhook');
|
||||
|
||||
let latest: string | null = null;
|
||||
|
||||
export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
||||
try {
|
||||
if (latest !== (latest = JSON.stringify(job.data.content, null, 2))) {
|
||||
logger.debug(`delivering ${latest}`);
|
||||
}
|
||||
logger.debug(`delivering ${job.data.webhookId}`);
|
||||
|
||||
const res = await getResponse({
|
||||
url: job.data.to,
|
||||
@ -25,7 +21,14 @@ export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
||||
'X-Misskey-Hook-Id': job.data.webhookId,
|
||||
'X-Misskey-Hook-Secret': job.data.secret,
|
||||
},
|
||||
body: JSON.stringify(job.data.content),
|
||||
body: JSON.stringify({
|
||||
hookId: job.data.webhookId,
|
||||
userId: job.data.userId,
|
||||
eventId: job.data.eventId,
|
||||
createdAt: job.data.createdAt,
|
||||
type: job.data.type,
|
||||
body: job.data.content,
|
||||
}),
|
||||
});
|
||||
|
||||
Webhooks.update({ id: job.data.webhookId }, {
|
||||
|
@ -48,10 +48,14 @@ export type EndedPollNotificationJobData = {
|
||||
};
|
||||
|
||||
export type WebhookDeliverJobData = {
|
||||
type: string;
|
||||
content: unknown;
|
||||
webhookId: Webhook['id'];
|
||||
userId: User['id'];
|
||||
to: string;
|
||||
secret: string;
|
||||
createdAt: number;
|
||||
eventId: string;
|
||||
};
|
||||
|
||||
export type ThinUser = {
|
||||
|
@ -95,7 +95,7 @@ function genSigningString(request: Request, includeHeaders: string[]) {
|
||||
|
||||
function lcObjectKey(src: Record<string, string>) {
|
||||
const dst: Record<string, string> = {};
|
||||
for (const key of Object.keys(src).filter(x => x != '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
|
||||
for (const key of Object.keys(src).filter(x => x !== '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
@ -109,15 +109,15 @@ export default class DeliverManager {
|
||||
}
|
||||
}
|
||||
|
||||
this.recipes.filter((recipe): recipe is IDirectRecipe => {
|
||||
this.recipes.filter((recipe): recipe is IDirectRecipe =>
|
||||
// followers recipes have already been processed
|
||||
isDirect(recipe)
|
||||
// check that shared inbox has not been added yet
|
||||
&& !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox))
|
||||
// check that they actually have an inbox
|
||||
&& recipe.to.inbox
|
||||
})
|
||||
.forEach(recipe => inboxes.add(recipe.to.inbox));
|
||||
&& recipe.to.inbox != null,
|
||||
)
|
||||
.forEach(recipe => inboxes.add(recipe.to.inbox!));
|
||||
|
||||
// deliver
|
||||
for (const inbox of inboxes) {
|
||||
|
@ -18,7 +18,7 @@ export const performReadActivity = async (actor: CacheableRemoteUser, activity:
|
||||
return `skip: message not found`;
|
||||
}
|
||||
|
||||
if (actor.id != message.recipientId) {
|
||||
if (actor.id !== message.recipientId) {
|
||||
return `skip: actor is not a message recipient`;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import unfollow from '@/services/following/delete.js';
|
||||
import cancelRequest from '@/services/following/requests/cancel.js';
|
||||
import {IAccept} from '../../type.js';
|
||||
import { IAccept } from '../../type.js';
|
||||
import { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||
import { Followings } from '@/models/index.js';
|
||||
import DbResolver from '../../db-resolver.js';
|
||||
|
@ -113,7 +113,8 @@ export class LdSignature {
|
||||
headers: {
|
||||
Accept: 'application/ld+json, application/json',
|
||||
},
|
||||
timeout: this.loderTimeout,
|
||||
// TODO
|
||||
//timeout: this.loderTimeout,
|
||||
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
|
||||
}).then(res => {
|
||||
if (!res.ok) {
|
||||
|
@ -69,7 +69,7 @@ export async function updateQuestion(value: any) {
|
||||
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
||||
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
||||
|
||||
if (oldCount != newCount) {
|
||||
if (oldCount !== newCount) {
|
||||
changed = true;
|
||||
poll.votes[poll.choices.indexOf(choice)] = newCount;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ type IWebFinger = {
|
||||
export default async function(query: string): Promise<IWebFinger> {
|
||||
const url = genUrl(query);
|
||||
|
||||
return await getJson(url, 'application/jrd+json, application/json');
|
||||
return await getJson(url, 'application/jrd+json, application/json') as IWebFinger;
|
||||
}
|
||||
|
||||
function genUrl(query: string) {
|
||||
|
@ -121,14 +121,14 @@ export function verifyLogin({
|
||||
signature: Buffer,
|
||||
challenge: string
|
||||
}) {
|
||||
if (clientData.type != 'webauthn.get') {
|
||||
if (clientData.type !== 'webauthn.get') {
|
||||
throw new Error('type is not webauthn.get');
|
||||
}
|
||||
|
||||
if (hash(clientData.challenge).toString('hex') != challenge) {
|
||||
if (hash(clientData.challenge).toString('hex') !== challenge) {
|
||||
throw new Error('challenge mismatch');
|
||||
}
|
||||
if (clientData.origin != config.scheme + '://' + config.host) {
|
||||
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||
throw new Error('origin mismatch');
|
||||
}
|
||||
|
||||
@ -148,11 +148,11 @@ export const procedures = {
|
||||
verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
|
||||
const negTwo = publicKey.get(-2);
|
||||
|
||||
if (!negTwo || negTwo.length != 32) {
|
||||
if (!negTwo || negTwo.length !== 32) {
|
||||
throw new Error('invalid or no -2 key given');
|
||||
}
|
||||
const negThree = publicKey.get(-3);
|
||||
if (!negThree || negThree.length != 32) {
|
||||
if (!negThree || negThree.length !== 32) {
|
||||
throw new Error('invalid or no -3 key given');
|
||||
}
|
||||
|
||||
@ -183,7 +183,7 @@ export const procedures = {
|
||||
rpIdHash: Buffer,
|
||||
credentialId: Buffer,
|
||||
}) {
|
||||
if (attStmt.alg != -7) {
|
||||
if (attStmt.alg !== -7) {
|
||||
throw new Error('alg mismatch');
|
||||
}
|
||||
|
||||
@ -196,11 +196,11 @@ export const procedures = {
|
||||
|
||||
const negTwo = publicKey.get(-2);
|
||||
|
||||
if (!negTwo || negTwo.length != 32) {
|
||||
if (!negTwo || negTwo.length !== 32) {
|
||||
throw new Error('invalid or no -2 key given');
|
||||
}
|
||||
const negThree = publicKey.get(-3);
|
||||
if (!negThree || negThree.length != 32) {
|
||||
if (!negThree || negThree.length !== 32) {
|
||||
throw new Error('invalid or no -3 key given');
|
||||
}
|
||||
|
||||
@ -263,7 +263,7 @@ export const procedures = {
|
||||
.map((key: any) => PEMString(key))
|
||||
.concat([GSR2]);
|
||||
|
||||
if (getCertSubject(certificateChain[0]).CN != 'attest.android.com') {
|
||||
if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') {
|
||||
throw new Error('invalid common name');
|
||||
}
|
||||
|
||||
@ -283,11 +283,11 @@ export const procedures = {
|
||||
|
||||
const negTwo = publicKey.get(-2);
|
||||
|
||||
if (!negTwo || negTwo.length != 32) {
|
||||
if (!negTwo || negTwo.length !== 32) {
|
||||
throw new Error('invalid or no -2 key given');
|
||||
}
|
||||
const negThree = publicKey.get(-3);
|
||||
if (!negThree || negThree.length != 32) {
|
||||
if (!negThree || negThree.length !== 32) {
|
||||
throw new Error('invalid or no -3 key given');
|
||||
}
|
||||
|
||||
@ -332,11 +332,11 @@ export const procedures = {
|
||||
|
||||
const negTwo = publicKey.get(-2);
|
||||
|
||||
if (!negTwo || negTwo.length != 32) {
|
||||
if (!negTwo || negTwo.length !== 32) {
|
||||
throw new Error('invalid or no -2 key given');
|
||||
}
|
||||
const negThree = publicKey.get(-3);
|
||||
if (!negThree || negThree.length != 32) {
|
||||
if (!negThree || negThree.length !== 32) {
|
||||
throw new Error('invalid or no -3 key given');
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ export const procedures = {
|
||||
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-ecdaa-algorithm-v2.0-id-20180227.html#ecdaa-verify-operation
|
||||
throw new Error('ECDAA-Verify is not supported');
|
||||
} else {
|
||||
if (attStmt.alg != -7) throw new Error('alg mismatch');
|
||||
if (attStmt.alg !== -7) throw new Error('alg mismatch');
|
||||
|
||||
throw new Error('self attestation is not supported');
|
||||
}
|
||||
@ -377,7 +377,7 @@ export const procedures = {
|
||||
credentialId: Buffer
|
||||
}) {
|
||||
const x5c: Buffer[] = attStmt.x5c;
|
||||
if (x5c.length != 1) {
|
||||
if (x5c.length !== 1) {
|
||||
throw new Error('x5c length does not match expectation');
|
||||
}
|
||||
|
||||
@ -387,11 +387,11 @@ export const procedures = {
|
||||
|
||||
const negTwo: Buffer = publicKey.get(-2);
|
||||
|
||||
if (!negTwo || negTwo.length != 32) {
|
||||
if (!negTwo || negTwo.length !== 32) {
|
||||
throw new Error('invalid or no -2 key given');
|
||||
}
|
||||
const negThree: Buffer = publicKey.get(-3);
|
||||
if (!negThree || negThree.length != 32) {
|
||||
if (!negThree || negThree.length !== 32) {
|
||||
throw new Error('invalid or no -3 key given');
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import { publishMainStream } from '@/services/stream.js';
|
||||
export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||
if (redirect) {
|
||||
//#region Cookie
|
||||
ctx.cookies.set('igi', user.token, {
|
||||
ctx.cookies.set('igi', user.token!, {
|
||||
path: '/',
|
||||
// SEE: https://github.com/koajs/koa/issues/974
|
||||
// When using a SSL proxy it should be configured to add the "X-Forwarded-Proto: https" header
|
||||
|
@ -27,7 +27,12 @@ export const paramDef = {
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
|
||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
||||
hostname: { type: 'string', nullable: true, default: null },
|
||||
hostname: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -40,6 +40,7 @@ export const meta = {
|
||||
userHost: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
md5: {
|
||||
type: 'string',
|
||||
@ -151,11 +152,20 @@ export const meta = {
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['fileId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: ['url'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -40,6 +40,7 @@ export const meta = {
|
||||
host: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
@ -54,7 +55,12 @@ export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', nullable: true, default: null },
|
||||
host: { type: 'string', nullable: true, default: null },
|
||||
host: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: 'Use `null` to represent the local host.',
|
||||
},
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -38,8 +38,9 @@ export const meta = {
|
||||
optional: false, nullable: true,
|
||||
},
|
||||
host: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
type: 'null',
|
||||
optional: false,
|
||||
description: 'The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files.',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
|
@ -17,7 +17,11 @@ export const paramDef = {
|
||||
ids: { type: 'array', items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
category: { type: 'string', nullable: true },
|
||||
category: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'Use `null` to reset the category.',
|
||||
},
|
||||
},
|
||||
required: ['ids'],
|
||||
} as const;
|
||||
|
@ -23,7 +23,11 @@ export const paramDef = {
|
||||
properties: {
|
||||
id: { type: 'string', format: 'misskey:id' },
|
||||
name: { type: 'string' },
|
||||
category: { type: 'string', nullable: true },
|
||||
category: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'Use `null` to reset the category.',
|
||||
},
|
||||
aliases: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
|
@ -26,8 +26,13 @@ export const paramDef = {
|
||||
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
|
||||
state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
|
||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
||||
username: { type: 'string', default: null },
|
||||
hostname: { type: 'string', default: null },
|
||||
username: { type: 'string', nullable: true, default: null },
|
||||
hostname: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
default: null,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -397,12 +397,14 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||
}
|
||||
|
||||
await db.transaction(async transactionalEntityManager => {
|
||||
const meta = await transactionalEntityManager.findOne(Meta, {
|
||||
const metas = await transactionalEntityManager.find(Meta, {
|
||||
order: {
|
||||
id: 'DESC',
|
||||
},
|
||||
});
|
||||
|
||||
const meta = metas[0];
|
||||
|
||||
if (meta) {
|
||||
await transactionalEntityManager.update(Meta, meta.id, set);
|
||||
} else {
|
||||
|
@ -57,13 +57,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
throw new ApiError(meta.errors.noSuchAntenna);
|
||||
}
|
||||
|
||||
const antennaQuery = AntennaNotes.createQueryBuilder('joining')
|
||||
.select('joining.noteId')
|
||||
.where('joining.antennaId = :antennaId', { antennaId: antenna.id });
|
||||
|
||||
const query = makePaginationQuery(Notes.createQueryBuilder('note'),
|
||||
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
|
||||
.andWhere(`note.id IN (${ antennaQuery.getQuery() })`)
|
||||
.innerJoin(AntennaNotes.metadata.targetName, 'antennaNote', 'antennaNote.noteId = note.id')
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||
.leftJoinAndSelect('user.banner', 'banner')
|
||||
@ -75,7 +71,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||
.setParameters(antennaQuery.getParameters());
|
||||
.andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id });
|
||||
|
||||
generateVisibilityQuery(query, user);
|
||||
generateMutedUserQuery(query, user);
|
||||
|
@ -20,7 +20,7 @@ export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||
isPublic: { type: 'boolean' },
|
||||
isPublic: { type: 'boolean', default: false },
|
||||
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
|
||||
},
|
||||
required: ['name'],
|
||||
|
@ -57,12 +57,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
throw new ApiError(meta.errors.noSuchClip);
|
||||
}
|
||||
|
||||
const clipQuery = ClipNotes.createQueryBuilder('joining')
|
||||
.select('joining.noteId')
|
||||
.where('joining.clipId = :clipId', { clipId: clip.id });
|
||||
|
||||
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere(`note.id IN (${ clipQuery.getQuery() })`)
|
||||
.innerJoin(ClipNotes.metadata.targetName, 'clipNote', 'clipNote.noteId = note.id')
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||
.leftJoinAndSelect('user.banner', 'banner')
|
||||
@ -74,7 +70,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||
.setParameters(clipQuery.getParameters());
|
||||
.andWhere('clipNote.clipId = :clipId', { clipId: clip.id });
|
||||
|
||||
if (user) {
|
||||
generateVisibilityQuery(query, user);
|
||||
|
@ -48,7 +48,6 @@ export const paramDef = {
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
// @ts-ignore
|
||||
export default define(meta, paramDef, async (ps, user, _, file, cleanup) => {
|
||||
// Get 'name' parameter
|
||||
let name = ps.name || file.originalname;
|
||||
|
@ -28,22 +28,25 @@ export const meta = {
|
||||
code: 'ACCESS_DENIED',
|
||||
id: '25b73c73-68b1-41d0-bad1-381cfdf6579f',
|
||||
},
|
||||
|
||||
fileIdOrUrlRequired: {
|
||||
message: 'fileId or url required.',
|
||||
code: 'INVALID_PARAM',
|
||||
id: '89674805-722c-440c-8d88-5641830dc3e4',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['fileId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
url: { type: 'string' },
|
||||
},
|
||||
required: ['url'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
@ -62,8 +65,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
thumbnailUrl: ps.url,
|
||||
}],
|
||||
});
|
||||
} else {
|
||||
throw new ApiError(meta.errors.fileIdOrUrlRequired);
|
||||
}
|
||||
|
||||
if (file == null) {
|
||||
|
@ -22,7 +22,7 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
host: { type: 'string', nullable: true },
|
||||
host: { type: 'string', nullable: true, description: 'Omit or use `null` to not filter by host.' },
|
||||
blocked: { type: 'boolean', nullable: true },
|
||||
notResponding: { type: 'boolean', nullable: true },
|
||||
suspended: { type: 'boolean', nullable: true },
|
||||
|
@ -50,10 +50,10 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
|
||||
const clientData = JSON.parse(ps.clientDataJSON);
|
||||
|
||||
if (clientData.type != 'webauthn.create') {
|
||||
if (clientData.type !== 'webauthn.create') {
|
||||
throw new Error('not a creation attestation');
|
||||
}
|
||||
if (clientData.origin != config.scheme + '://' + config.host) {
|
||||
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||
throw new Error('origin mismatch');
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
const credentialId = authData.slice(55, 55 + credentialIdLength);
|
||||
const publicKeyData = authData.slice(55 + credentialIdLength);
|
||||
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
||||
if (publicKey.get(3) != -7) {
|
||||
if (publicKey.get(3) !== -7) {
|
||||
throw new Error('alg mismatch');
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
take: ps.limit,
|
||||
skip: ps.offset,
|
||||
order: {
|
||||
id: ps.sort == 'asc' ? 1 : -1,
|
||||
id: ps.sort === 'asc' ? 1 : -1,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -47,14 +47,25 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
groupId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
markAsRead: { type: 'boolean', default: true },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
groupId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['groupId'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
@ -126,7 +137,5 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {
|
||||
populateGroup: false,
|
||||
})));
|
||||
} else {
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
|
@ -67,12 +67,23 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
groupId: { type: 'string', format: 'misskey:id' },
|
||||
text: { type: 'string', nullable: true, maxLength: 3000 },
|
||||
fileId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
groupId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['groupId'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -169,6 +169,7 @@ export const meta = {
|
||||
host: {
|
||||
type: 'string',
|
||||
optional: false, nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
|
@ -38,7 +38,11 @@ export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
expiresAt: { type: 'integer', nullable: true },
|
||||
expiresAt: {
|
||||
type: 'integer',
|
||||
nullable: true,
|
||||
description: 'A Unix Epoch timestamp that must lie in the future. `null` means an indefinite mute.',
|
||||
},
|
||||
},
|
||||
required: ['userId'],
|
||||
} as const;
|
||||
|
@ -19,7 +19,7 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
local: { type: 'boolean' },
|
||||
local: { type: 'boolean', default: false },
|
||||
reply: { type: 'boolean' },
|
||||
renote: { type: 'boolean' },
|
||||
withFiles: { type: 'boolean' },
|
||||
@ -52,19 +52,19 @@ export default define(meta, paramDef, async (ps) => {
|
||||
query.andWhere('note.userHost IS NULL');
|
||||
}
|
||||
|
||||
if (ps.reply != undefined) {
|
||||
if (ps.reply !== undefined) {
|
||||
query.andWhere(ps.reply ? 'note.replyId IS NOT NULL' : 'note.replyId IS NULL');
|
||||
}
|
||||
|
||||
if (ps.renote != undefined) {
|
||||
if (ps.renote !== undefined) {
|
||||
query.andWhere(ps.renote ? 'note.renoteId IS NOT NULL' : 'note.renoteId IS NULL');
|
||||
}
|
||||
|
||||
if (ps.withFiles != undefined) {
|
||||
if (ps.withFiles !== undefined) {
|
||||
query.andWhere(ps.withFiles ? `note.fileIds != '{}'` : `note.fileIds = '{}'`);
|
||||
}
|
||||
|
||||
if (ps.poll != undefined) {
|
||||
if (ps.poll !== undefined) {
|
||||
query.andWhere(ps.poll ? 'note.hasPoll = TRUE' : 'note.hasPoll = FALSE');
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
conversation.push(p);
|
||||
}
|
||||
|
||||
if (conversation.length == ps.limit) {
|
||||
if (conversation.length === ps.limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import { Note } from '@/models/entities/note.js';
|
||||
import { noteVisibilities } from '../../../../types.js';
|
||||
import { Channel } from '@/models/entities/channel.js';
|
||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
@ -59,12 +60,6 @@ export const meta = {
|
||||
id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
|
||||
},
|
||||
|
||||
contentRequired: {
|
||||
message: 'Content required. You need to set text, fileIds, renoteId or poll.',
|
||||
code: 'CONTENT_REQUIRED',
|
||||
id: '6f57e42b-c348-439b-bc45-993995cc515a',
|
||||
},
|
||||
|
||||
cannotCreateAlreadyExpiredPoll: {
|
||||
message: 'Poll is already expired.',
|
||||
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
|
||||
@ -92,29 +87,41 @@ export const paramDef = {
|
||||
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
text: { type: 'string', nullable: true, maxLength: MAX_NOTE_TEXT_LENGTH, default: null },
|
||||
text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true },
|
||||
cw: { type: 'string', nullable: true, maxLength: 100 },
|
||||
localOnly: { type: 'boolean', default: false },
|
||||
noExtractMentions: { type: 'boolean', default: false },
|
||||
noExtractHashtags: { type: 'boolean', default: false },
|
||||
noExtractEmojis: { type: 'boolean', default: false },
|
||||
fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
mediaIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
fileIds: {
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
maxItems: 16,
|
||||
items: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
mediaIds: {
|
||||
deprecated: true,
|
||||
description: 'Use `fileIds` instead. If both are specified, this property is discarded.',
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 1,
|
||||
maxItems: 16,
|
||||
items: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||
poll: {
|
||||
type: 'object', nullable: true,
|
||||
type: 'object',
|
||||
nullable: true,
|
||||
properties: {
|
||||
choices: {
|
||||
type: 'array', uniqueItems: true, minItems: 2, maxItems: 10,
|
||||
items: {
|
||||
type: 'string', minLength: 1, maxLength: 50,
|
||||
},
|
||||
type: 'array',
|
||||
uniqueItems: true,
|
||||
minItems: 2,
|
||||
maxItems: 10,
|
||||
items: { type: 'string', minLength: 1, maxLength: 50 },
|
||||
},
|
||||
multiple: { type: 'boolean', default: false },
|
||||
expiresAt: { type: 'integer', nullable: true },
|
||||
@ -123,26 +130,52 @@ export const paramDef = {
|
||||
required: ['choices'],
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
// (re)note with text, files and poll are optional
|
||||
properties: {
|
||||
text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: false },
|
||||
},
|
||||
required: ['text'],
|
||||
},
|
||||
{
|
||||
// (re)note with files, text and poll are optional
|
||||
required: ['fileIds'],
|
||||
},
|
||||
{
|
||||
// (re)note with files, text and poll are optional
|
||||
required: ['mediaIds'],
|
||||
},
|
||||
{
|
||||
// (re)note with poll, text and files are optional
|
||||
properties: {
|
||||
poll: { type: 'object', nullable: false, },
|
||||
},
|
||||
required: ['poll'],
|
||||
},
|
||||
{
|
||||
// pure renote
|
||||
required: ['renoteId'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default define(meta, paramDef, async (ps, user) => {
|
||||
let visibleUsers: User[] = [];
|
||||
if (ps.visibleUserIds) {
|
||||
visibleUsers = (await Promise.all(ps.visibleUserIds.map(id => Users.findOneBy({ id }))))
|
||||
.filter(x => x != null) as User[];
|
||||
visibleUsers = await Users.findBy({
|
||||
id: In(ps.visibleUserIds),
|
||||
});
|
||||
}
|
||||
|
||||
let files: DriveFile[] = [];
|
||||
const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
|
||||
if (fileIds != null) {
|
||||
files = (await Promise.all(fileIds.map(fileId =>
|
||||
DriveFiles.findOneBy({
|
||||
id: fileId,
|
||||
userId: user.id,
|
||||
})
|
||||
))).filter(file => file != null) as DriveFile[];
|
||||
files = await DriveFiles.findBy({
|
||||
userId: user.id,
|
||||
id: In(fileIds),
|
||||
});
|
||||
}
|
||||
|
||||
let renote: Note | null;
|
||||
@ -152,7 +185,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
|
||||
if (renote == null) {
|
||||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
||||
} else if (renote.renoteId && !renote.text && !renote.fileIds) {
|
||||
} else if (renote.renoteId && !renote.text && !renote.fileIds && !renote.poll) {
|
||||
throw new ApiError(meta.errors.cannotReRenote);
|
||||
}
|
||||
|
||||
@ -175,10 +208,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
|
||||
if (reply == null) {
|
||||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||
}
|
||||
|
||||
// 返信対象が引用でないRenoteだったらエラー
|
||||
if (reply.renoteId && !reply.text && !reply.fileIds) {
|
||||
} else if (reply.renoteId && !reply.text && !reply.fileIds && !renote.poll) {
|
||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
||||
}
|
||||
|
||||
@ -204,11 +234,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
}
|
||||
}
|
||||
|
||||
// テキストが無いかつ添付ファイルが無いかつRenoteも無いかつ投票も無かったらエラー
|
||||
if (!(ps.text || files.length || renote || ps.poll)) {
|
||||
throw new ApiError(meta.errors.contentRequired);
|
||||
}
|
||||
|
||||
let channel: Channel | undefined;
|
||||
if (ps.channelId != null) {
|
||||
channel = await Channels.findOneBy({ id: ps.channelId });
|
||||
|
@ -35,7 +35,11 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
|
@ -48,7 +48,11 @@ export const paramDef = {
|
||||
includeMyRenotes: { type: 'boolean', default: true },
|
||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||
includeLocalRenotes: { type: 'boolean', default: true },
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -37,7 +37,11 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
fileType: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
|
@ -110,7 +110,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
|
||||
if (exist.length) {
|
||||
if (poll.multiple) {
|
||||
if (exist.some(x => x.choice == ps.choice)) {
|
||||
if (exist.some(x => x.choice === ps.choice)) {
|
||||
throw new ApiError(meta.errors.alreadyVoted);
|
||||
}
|
||||
} else {
|
||||
|
@ -25,21 +25,44 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
tag: { type: 'string' },
|
||||
query: { type: 'array', items: {
|
||||
type: 'array', items: {
|
||||
type: 'string',
|
||||
},
|
||||
} },
|
||||
reply: { type: 'boolean', nullable: true, default: null },
|
||||
renote: { type: 'boolean', nullable: true, default: null },
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
poll: { type: 'boolean', nullable: true, default: null },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
tag: { type: 'string', minLength: 1 },
|
||||
},
|
||||
required: ['tag'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
query: {
|
||||
type: 'array',
|
||||
description: 'The outer arrays are chained with OR, the inner arrays are chained with AND.',
|
||||
items: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
},
|
||||
minItems: 1,
|
||||
},
|
||||
minItems: 1,
|
||||
},
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -35,7 +35,11 @@ export const paramDef = {
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
offset: { type: 'integer', default: 0 },
|
||||
host: { type: 'string', nullable: true },
|
||||
host: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||
channelId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||
},
|
||||
|
@ -38,7 +38,11 @@ export const paramDef = {
|
||||
includeMyRenotes: { type: 'boolean', default: true },
|
||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||
includeLocalRenotes: { type: 'boolean', default: true },
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
@ -75,7 +75,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
Accept: 'application/json, */*',
|
||||
},
|
||||
body: params,
|
||||
timeout: 10000,
|
||||
// TODO
|
||||
//timeout: 10000,
|
||||
agent: getAgentByUrl,
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,11 @@ export const paramDef = {
|
||||
includeMyRenotes: { type: 'boolean', default: true },
|
||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||
includeLocalRenotes: { type: 'boolean', default: true },
|
||||
withFiles: { type: 'boolean' },
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
},
|
||||
required: ['listId'],
|
||||
} as const;
|
||||
@ -59,12 +63,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
}
|
||||
|
||||
//#region Construct query
|
||||
const listQuery = UserListJoinings.createQueryBuilder('joining')
|
||||
.select('joining.userId')
|
||||
.where('joining.userListId = :userListId', { userListId: list.id });
|
||||
|
||||
const query = makePaginationQuery(Notes.createQueryBuilder('note'), ps.sinceId, ps.untilId)
|
||||
.andWhere(`note.userId IN (${ listQuery.getQuery() })`)
|
||||
.innerJoin(UserListJoinings.metadata.targetName, 'userListJoining', 'userListJoining.userId = note.userId')
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||
.leftJoinAndSelect('user.banner', 'banner')
|
||||
@ -76,7 +76,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||
.setParameters(listQuery.getParameters());
|
||||
.andWhere('userListJoining.userListId = :userListId', { userListId: list.id });
|
||||
|
||||
generateVisibilityQuery(query, user);
|
||||
|
||||
|
@ -26,12 +26,21 @@ export const meta = {
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pageId: { type: 'string', format: 'misskey:id' },
|
||||
name: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
pageId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['pageId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
username: { type: 'string' },
|
||||
},
|
||||
required: ['name', 'username'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -38,14 +38,29 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
username: { type: 'string' },
|
||||
host: { type: 'string', nullable: true },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
username: { type: 'string' },
|
||||
host: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
},
|
||||
required: ['username', 'host'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -38,14 +38,29 @@ export const meta = {
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
username: { type: 'string' },
|
||||
host: { type: 'string', nullable: true },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
username: { type: 'string' },
|
||||
host: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
},
|
||||
required: ['username', 'host'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -28,7 +28,10 @@ export const paramDef = {
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
detail: { type: 'boolean', default: true },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{ required: ['username'] },
|
||||
{ required: ['host'] },
|
||||
],
|
||||
} as const;
|
||||
|
||||
// TODO: avatar,bannerをJOINしたいけどエラーになる
|
||||
|
@ -23,9 +23,9 @@ export const meta = {
|
||||
items: {
|
||||
type: 'object',
|
||||
ref: 'UserDetailed',
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
|
||||
errors: {
|
||||
@ -46,15 +46,33 @@ export const meta = {
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
userIds: { type: 'array', uniqueItems: true, items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
username: { type: 'string' },
|
||||
host: { type: 'string', nullable: true },
|
||||
},
|
||||
required: [],
|
||||
anyOf: [
|
||||
{
|
||||
properties: {
|
||||
userId: { type: 'string', format: 'misskey:id' },
|
||||
},
|
||||
required: ['userId'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
userIds: { type: 'array', uniqueItems: true, items: {
|
||||
type: 'string', format: 'misskey:id',
|
||||
} },
|
||||
},
|
||||
required: ['userIds'],
|
||||
},
|
||||
{
|
||||
properties: {
|
||||
username: { type: 'string' },
|
||||
host: {
|
||||
type: 'string',
|
||||
nullable: true,
|
||||
description: 'The local host is represented with `null`.',
|
||||
},
|
||||
},
|
||||
required: ['username'],
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
|
@ -24,17 +24,17 @@ export default async (ctx: Koa.Context) => {
|
||||
ctx.body = { error };
|
||||
}
|
||||
|
||||
if (typeof username != 'string') {
|
||||
if (typeof username !== 'string') {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof password != 'string') {
|
||||
if (typeof password !== 'string') {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|
||||
if (token != null && typeof token != 'string') {
|
||||
if (token != null && typeof token !== 'string') {
|
||||
ctx.status = 400;
|
||||
return;
|
||||
}
|
||||
|