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
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
- uses: actions/setup-node@v1
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 12.x
|
node-version: 16.x
|
||||||
- uses: actions/cache@v2
|
cache: 'yarn'
|
||||||
with:
|
cache-dependency-path: |
|
||||||
path: '**/node_modules'
|
packages/backend/yarn.lock
|
||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
packages/client/yarn.lock
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
- run: yarn lint
|
- run: yarn lint
|
||||||
|
84
.github/workflows/test.yml
vendored
@ -8,44 +8,48 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
#mocha:
|
mocha:
|
||||||
# runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
# strategy:
|
strategy:
|
||||||
# matrix:
|
matrix:
|
||||||
# node-version: [16.x]
|
node-version: [16.x]
|
||||||
|
|
||||||
# services:
|
services:
|
||||||
# postgres:
|
postgres:
|
||||||
# image: postgres:13
|
image: postgres:13
|
||||||
# ports:
|
ports:
|
||||||
# - 54312:5432
|
- 54312:5432
|
||||||
# env:
|
env:
|
||||||
# POSTGRES_DB: test-misskey
|
POSTGRES_DB: test-misskey
|
||||||
# POSTGRES_HOST_AUTH_METHOD: trust
|
POSTGRES_HOST_AUTH_METHOD: trust
|
||||||
# redis:
|
redis:
|
||||||
# image: redis:6
|
image: redis:6
|
||||||
# ports:
|
ports:
|
||||||
# - 56312:6379
|
- 56312:6379
|
||||||
|
|
||||||
# steps:
|
steps:
|
||||||
# - uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
# with:
|
with:
|
||||||
# submodules: true
|
submodules: true
|
||||||
# - name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
# uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
# with:
|
with:
|
||||||
# node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
# - name: Install dependencies
|
cache: 'yarn'
|
||||||
# run: yarn install
|
cache-dependency-path: |
|
||||||
# - name: Check yarn.lock
|
packages/backend/yarn.lock
|
||||||
# run: git diff --exit-code yarn.lock
|
packages/client/yarn.lock
|
||||||
# - name: Copy Configure
|
- name: Install dependencies
|
||||||
# run: cp .github/misskey/test.yml .config
|
run: yarn install
|
||||||
# - name: Build
|
- name: Check yarn.lock
|
||||||
# run: yarn build
|
run: git diff --exit-code yarn.lock
|
||||||
# - name: Test
|
- name: Copy Configure
|
||||||
# run: yarn mocha
|
run: cp .github/misskey/test.yml .config
|
||||||
|
- name: Build
|
||||||
|
run: yarn build
|
||||||
|
- name: Test
|
||||||
|
run: yarn mocha
|
||||||
|
|
||||||
e2e:
|
e2e:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -80,13 +84,13 @@ jobs:
|
|||||||
#- uses: browser-actions/setup-firefox@latest
|
#- uses: browser-actions/setup-firefox@latest
|
||||||
# if: ${{ matrix.browser == 'firefox' }}
|
# if: ${{ matrix.browser == 'firefox' }}
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- uses: actions/cache@v2
|
cache: 'yarn'
|
||||||
with:
|
cache-dependency-path: |
|
||||||
path: '**/node_modules'
|
packages/backend/yarn.lock
|
||||||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }}
|
packages/client/yarn.lock
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
- name: Check yarn.lock
|
- name: Check yarn.lock
|
||||||
|
2
.gitignore
vendored
@ -42,5 +42,3 @@ ormconfig.json
|
|||||||
*.blend3
|
*.blend3
|
||||||
*.blend4
|
*.blend4
|
||||||
*.blend5
|
*.blend5
|
||||||
|
|
||||||
start.sh
|
|
22
CHANGELOG.md
@ -10,6 +10,28 @@
|
|||||||
You should also include the user name that made the change.
|
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)
|
## 12.109.1 (2022/04/02)
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
|
@ -32,4 +32,3 @@ COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node
|
|||||||
COPY . ./
|
COPY . ./
|
||||||
|
|
||||||
CMD ["npm", "run", "migrateandstart"]
|
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">
|
## 本家との違い
|
||||||
|
[ここ](https://nullc.at/nca10.net/)にかいてあるよ
|
||||||
**🌎 A forever evolving, interplanetary microblogging platform. 🚀**
|
## 導入方法
|
||||||
|
[MisskeyHub](https://misskey-hub.net/docs/install.html)をみてね
|
||||||
**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
|
|
||||||
|
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,50 +1,29 @@
|
|||||||
version: "3"
|
version: "3"
|
||||||
|
|
||||||
services:
|
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:
|
db:
|
||||||
restart: always
|
restart: always
|
||||||
image: postgres:12.2-alpine
|
image: postgres:12.2-alpine
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
networks:
|
networks:
|
||||||
- internal_network
|
- internal_network
|
||||||
|
- external_network
|
||||||
env_file:
|
env_file:
|
||||||
- .config/docker.env
|
- .config/docker.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./db:/var/lib/postgresql/data
|
- ./db:/var/lib/postgresql/data
|
||||||
|
|
||||||
# es:
|
redis:
|
||||||
# restart: always
|
restart: always
|
||||||
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.4.2
|
image: redis:4.0-alpine
|
||||||
# environment:
|
ports:
|
||||||
# - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
- "6379:6379"
|
||||||
# - "TAKE_FILE_OWNERSHIP=111"
|
networks:
|
||||||
# networks:
|
- internal_network
|
||||||
# - internal_network
|
- external_network
|
||||||
# volumes:
|
volumes:
|
||||||
# - ./elasticsearch:/usr/share/elasticsearch/data
|
- ./redis:/data
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
internal_network:
|
internal_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: "قوس قزح"
|
rainbow: "قوس قزح"
|
||||||
rainbowDescription: "اجعل المحتوى يظهر بألوان الطيف"
|
rainbowDescription: "اجعل المحتوى يظهر بألوان الطيف"
|
||||||
rotate: "تدوير"
|
rotate: "تدوير"
|
||||||
|
rotateDescription: "يُدير المحتوى بزاوية معيّنة."
|
||||||
_instanceTicker:
|
_instanceTicker:
|
||||||
none: "لا تظهره بتاتًا"
|
none: "لا تظهره بتاتًا"
|
||||||
remote: "أظهر للمستخدمين البِعاد"
|
remote: "أظهر للمستخدمين البِعاد"
|
||||||
@ -1236,6 +1237,8 @@ _pages:
|
|||||||
font: "الخط"
|
font: "الخط"
|
||||||
fontSerif: "Serif"
|
fontSerif: "Serif"
|
||||||
fontSansSerif: "Sans Serif"
|
fontSansSerif: "Sans Serif"
|
||||||
|
eyeCatchingImageSet: "عيّن صورة مصغّرة"
|
||||||
|
eyeCatchingImageRemove: "احذف صورة مصغّرة"
|
||||||
chooseBlock: "إضافة كتلة"
|
chooseBlock: "إضافة كتلة"
|
||||||
selectType: "اختر النوع"
|
selectType: "اختر النوع"
|
||||||
enterVariableName: "أدخل اسم المتغيّر"
|
enterVariableName: "أدخل اسم المتغيّر"
|
||||||
@ -1496,6 +1499,7 @@ _notification:
|
|||||||
pollVote: "مصوِت شارك في الاستطلاع"
|
pollVote: "مصوِت شارك في الاستطلاع"
|
||||||
receiveFollowRequest: "طلبات المتابعة المتلقاة"
|
receiveFollowRequest: "طلبات المتابعة المتلقاة"
|
||||||
followRequestAccepted: "طلبات المتابعة المقبولة"
|
followRequestAccepted: "طلبات المتابعة المقبولة"
|
||||||
|
groupInvited: "دعوات الفريق"
|
||||||
app: "إشعارات التطبيقات المرتبطة"
|
app: "إشعارات التطبيقات المرتبطة"
|
||||||
_deck:
|
_deck:
|
||||||
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا"
|
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا"
|
||||||
|
@ -832,6 +832,10 @@ size: "আকার"
|
|||||||
numberOfColumn: "কলামের সংখ্যা"
|
numberOfColumn: "কলামের সংখ্যা"
|
||||||
searchByGoogle: "গুগল"
|
searchByGoogle: "গুগল"
|
||||||
indefinitely: "অনির্দিষ্ট"
|
indefinitely: "অনির্দিষ্ট"
|
||||||
|
tenMinutes: "১০ মিনিট"
|
||||||
|
oneHour: "১ ঘণ্টা"
|
||||||
|
oneDay: "একদিন"
|
||||||
|
oneWeek: "এক সপ্তাহ"
|
||||||
_emailUnavailable:
|
_emailUnavailable:
|
||||||
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
|
used: "এই ইমেইল ঠিকানাটি ইতোমধ্যে ব্যবহৃত হয়েছে"
|
||||||
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
|
format: "এই ইমেল ঠিকানাটি সঠিকভাবে লিখা হয়নি"
|
||||||
|
@ -94,6 +94,8 @@ unfollow: "Unfollow"
|
|||||||
followRequestPending: "Follow request pending"
|
followRequestPending: "Follow request pending"
|
||||||
enterEmoji: "Enter an emoji"
|
enterEmoji: "Enter an emoji"
|
||||||
renote: "Renote"
|
renote: "Renote"
|
||||||
|
renoteHomeOnly: "Renote to home only"
|
||||||
|
renoteFollowersOnly: "Renote to followers only"
|
||||||
unrenote: "Take back renote"
|
unrenote: "Take back renote"
|
||||||
renoted: "Renoted."
|
renoted: "Renoted."
|
||||||
cantRenote: "This post can't be renoted."
|
cantRenote: "This post can't be renoted."
|
||||||
|
1151
locales/eo-UY.yml
@ -19,12 +19,10 @@ const languages = [
|
|||||||
'da-DK',
|
'da-DK',
|
||||||
'de-DE',
|
'de-DE',
|
||||||
'en-US',
|
'en-US',
|
||||||
'eo-UY',
|
|
||||||
'es-ES',
|
'es-ES',
|
||||||
'fr-FR',
|
'fr-FR',
|
||||||
'id-ID',
|
'id-ID',
|
||||||
'it-IT',
|
'it-IT',
|
||||||
'ja-NY',
|
|
||||||
'ja-JP',
|
'ja-JP',
|
||||||
'ja-KS',
|
'ja-KS',
|
||||||
'kab-KAB',
|
'kab-KAB',
|
||||||
@ -58,7 +56,6 @@ module.exports = Object.entries(locales)
|
|||||||
const [lang] = k.split('-');
|
const [lang] = k.split('-');
|
||||||
switch (k) {
|
switch (k) {
|
||||||
case 'ja-JP': return v;
|
case 'ja-JP': return v;
|
||||||
case 'ja-NY':
|
|
||||||
case 'ja-KS':
|
case 'ja-KS':
|
||||||
case 'en-US': return merge(locales['ja-JP'], v);
|
case 'en-US': return merge(locales['ja-JP'], v);
|
||||||
default: return merge(
|
default: return merge(
|
||||||
|
1793
locales/ja-NY.yml
@ -481,13 +481,24 @@ showFeaturedNotesInTimeline: "Arată notele recomandate în cronologii"
|
|||||||
objectStorage: "Object Storage"
|
objectStorage: "Object Storage"
|
||||||
useObjectStorage: "Folosește Object Storage"
|
useObjectStorage: "Folosește Object Storage"
|
||||||
objectStorageBaseUrl: "URL de bază"
|
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"
|
objectStorageBucket: "Bucket"
|
||||||
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
|
objectStorageBucketDesc: "Te rog specifică numele bucket-ului furnizorului tău."
|
||||||
objectStoragePrefix: "Prefix"
|
objectStoragePrefix: "Prefix"
|
||||||
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
|
objectStoragePrefixDesc: "Fișierele vor fi stocate sub directoare cu acest prefix."
|
||||||
objectStorageEndpoint: "Endpoint"
|
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"
|
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"
|
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"
|
sounds: "Sunete"
|
||||||
listen: "Ascultă"
|
listen: "Ascultă"
|
||||||
none: "Nimic"
|
none: "Nimic"
|
||||||
@ -522,12 +533,42 @@ removeAllFollowingDescription: "Asta va dez-urmări toate conturile din {host}.
|
|||||||
userSuspended: "Acest utilizator a fost suspendat."
|
userSuspended: "Acest utilizator a fost suspendat."
|
||||||
userSilenced: "Acest utilizator a fost setat silențios."
|
userSilenced: "Acest utilizator a fost setat silențios."
|
||||||
yourAccountSuspendedTitle: "Acest cont a fost suspendat"
|
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ă"
|
smtpHost: "Gazdă"
|
||||||
smtpUser: "Nume de utilizator"
|
smtpUser: "Nume de utilizator"
|
||||||
smtpPass: "Parolă"
|
smtpPass: "Parolă"
|
||||||
clearCache: "Golește cache-ul"
|
clearCache: "Golește cache-ul"
|
||||||
info: "Despre"
|
info: "Despre"
|
||||||
user: "Utilizatori"
|
user: "Utilizatori"
|
||||||
|
administration: "Gestionare"
|
||||||
searchByGoogle: "Caută"
|
searchByGoogle: "Caută"
|
||||||
_email:
|
_email:
|
||||||
_follow:
|
_follow:
|
||||||
@ -538,9 +579,11 @@ _mfm:
|
|||||||
emoji: "Emoji personalizat"
|
emoji: "Emoji personalizat"
|
||||||
search: "Caută"
|
search: "Caută"
|
||||||
_theme:
|
_theme:
|
||||||
|
description: "Descriere"
|
||||||
keys:
|
keys:
|
||||||
mention: "Mențiune"
|
mention: "Mențiune"
|
||||||
renote: "Re-notează"
|
renote: "Re-notează"
|
||||||
|
divider: "Separator"
|
||||||
_sfx:
|
_sfx:
|
||||||
note: "Note"
|
note: "Note"
|
||||||
notification: "Notificări"
|
notification: "Notificări"
|
||||||
|
@ -81,6 +81,8 @@ somethingHappened: "發生錯誤"
|
|||||||
retry: "重試"
|
retry: "重試"
|
||||||
pageLoadError: "載入頁面失敗"
|
pageLoadError: "載入頁面失敗"
|
||||||
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
|
pageLoadErrorDescription: "這通常是因為網路錯誤或是瀏覽器快取殘留的原因。請先清除瀏覽器快取,稍後再重試"
|
||||||
|
serverIsDead: "伺服器沒有回應。請稍等片刻,然後重試。"
|
||||||
|
youShouldUpgradeClient: "請重新載入以使用新版本的客戶端顯示此頁面"
|
||||||
enterListName: "輸入清單名稱"
|
enterListName: "輸入清單名稱"
|
||||||
privacy: "隱私"
|
privacy: "隱私"
|
||||||
makeFollowManuallyApprove: "手動審核追隨請求"
|
makeFollowManuallyApprove: "手動審核追隨請求"
|
||||||
@ -104,6 +106,7 @@ clickToShow: "按一下以顯示"
|
|||||||
sensitive: "敏感內容"
|
sensitive: "敏感內容"
|
||||||
add: "新增"
|
add: "新增"
|
||||||
reaction: "情感"
|
reaction: "情感"
|
||||||
|
reactionSetting: "在選擇器中顯示反應"
|
||||||
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
|
reactionSettingDescription2: "拖動以重新列序,點擊以刪除,按下 + 添加。"
|
||||||
rememberNoteVisibility: "記住貼文可見性"
|
rememberNoteVisibility: "記住貼文可見性"
|
||||||
attachCancel: "移除附件"
|
attachCancel: "移除附件"
|
||||||
@ -138,6 +141,7 @@ flagAsBot: "此使用者是機器人"
|
|||||||
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
|
flagAsBotDescription: "如果本帳戶是由程式控制,請啟用此選項。啟用後,會作為標示幫助其他開發者防止機器人之間產生無限互動的行為,並會調整Misskey內部系統將本帳戶識別為機器人"
|
||||||
flagAsCat: "此使用者是貓"
|
flagAsCat: "此使用者是貓"
|
||||||
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
|
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
|
||||||
|
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
|
||||||
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
|
autoAcceptFollowed: "自動追隨中使用者的追隨請求"
|
||||||
addAccount: "添加帳戶"
|
addAccount: "添加帳戶"
|
||||||
loginFailed: "登入失敗"
|
loginFailed: "登入失敗"
|
||||||
@ -599,6 +603,9 @@ reportAbuse: "檢舉"
|
|||||||
reportAbuseOf: "檢舉{name}"
|
reportAbuseOf: "檢舉{name}"
|
||||||
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話,請附上針對的URL網址。"
|
fillAbuseReportDescription: "請填寫檢舉的詳細理由。可以的話,請附上針對的URL網址。"
|
||||||
abuseReported: "回報已送出。感謝您的報告。"
|
abuseReported: "回報已送出。感謝您的報告。"
|
||||||
|
reporter: "檢舉者"
|
||||||
|
reporteeOrigin: "檢舉來源"
|
||||||
|
reporterOrigin: "檢舉者來源"
|
||||||
send: "發送"
|
send: "發送"
|
||||||
abuseMarkAsResolved: "處理完畢"
|
abuseMarkAsResolved: "處理完畢"
|
||||||
openInNewTab: "在新分頁中開啟"
|
openInNewTab: "在新分頁中開啟"
|
||||||
@ -734,6 +741,7 @@ postToGallery: "發佈到相簿"
|
|||||||
gallery: "相簿"
|
gallery: "相簿"
|
||||||
recentPosts: "最新貼文"
|
recentPosts: "最新貼文"
|
||||||
popularPosts: "熱門的貼文"
|
popularPosts: "熱門的貼文"
|
||||||
|
shareWithNote: "在貼文中分享"
|
||||||
ads: "廣告"
|
ads: "廣告"
|
||||||
expiration: "期限"
|
expiration: "期限"
|
||||||
memo: "備忘錄"
|
memo: "備忘錄"
|
||||||
@ -743,14 +751,35 @@ middle: "中"
|
|||||||
low: "低"
|
low: "低"
|
||||||
emailNotConfiguredWarning: "沒有設定電子郵件地址"
|
emailNotConfiguredWarning: "沒有設定電子郵件地址"
|
||||||
ratio: "%"
|
ratio: "%"
|
||||||
|
previewNoteText: "預覽文本"
|
||||||
|
customCss: "自定義 CSS"
|
||||||
global: "公開"
|
global: "公開"
|
||||||
sent: "發送"
|
sent: "發送"
|
||||||
|
received: "收取"
|
||||||
|
searchResult: "搜尋結果"
|
||||||
hashtags: "#tag"
|
hashtags: "#tag"
|
||||||
|
troubleshooting: "故障排除"
|
||||||
|
useBlurEffect: "在 UI 上使用模糊效果"
|
||||||
|
misskeyUpdated: "Misskey 更新完成!"
|
||||||
|
translate: "翻譯"
|
||||||
|
translatedFrom: "從 {x} 翻譯"
|
||||||
|
accountDeletionInProgress: "正在刪除帳戶"
|
||||||
|
pubSub: "Pub/Sub 帳戶"
|
||||||
|
resolved: "已解決"
|
||||||
|
unresolved: "未解決"
|
||||||
|
breakFollow: "移除追蹤者"
|
||||||
hide: "隱藏"
|
hide: "隱藏"
|
||||||
|
leaveGroupConfirm: "確定離開「{name}」?"
|
||||||
|
auto: "自動"
|
||||||
searchByGoogle: "搜尋"
|
searchByGoogle: "搜尋"
|
||||||
indefinitely: "無期限"
|
indefinitely: "無期限"
|
||||||
_ffVisibility:
|
_ffVisibility:
|
||||||
public: "發佈"
|
public: "發佈"
|
||||||
|
private: "私密"
|
||||||
|
_signup:
|
||||||
|
almostThere: "即將完成"
|
||||||
|
_accountDelete:
|
||||||
|
inProgress: "正在刪除"
|
||||||
_ad:
|
_ad:
|
||||||
back: "返回"
|
back: "返回"
|
||||||
reduceFrequencyOfThisAd: "降低此廣告的頻率 "
|
reduceFrequencyOfThisAd: "降低此廣告的頻率 "
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "12.109.1-simkey-v1",
|
"version": "12.110.1-nca10.net-v1",
|
||||||
"codename": "indigo",
|
"codename": "indigo",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sim1222/misskey.git"
|
"url": "https://github.com/misskey-dev/misskey.git"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"js-yaml": "4.1.0"
|
"js-yaml": "4.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/parser": "5.17.0",
|
"@typescript-eslint/parser": "5.18.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "9.5.3",
|
"cypress": "9.5.3",
|
||||||
"start-server-and-test": "1.14.0",
|
"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": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
|
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
|
||||||
"watch": "node watch.mjs",
|
"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",
|
"mocha": "cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
|
||||||
"test": "npm run mocha"
|
"test": "npm run mocha"
|
||||||
},
|
},
|
||||||
@ -55,25 +55,24 @@
|
|||||||
"@types/redis": "4.0.11",
|
"@types/redis": "4.0.11",
|
||||||
"@types/rename": "1.0.4",
|
"@types/rename": "1.0.4",
|
||||||
"@types/sanitize-html": "2.6.2",
|
"@types/sanitize-html": "2.6.2",
|
||||||
"@types/sharp": "0.30.0",
|
"@types/sharp": "0.30.1",
|
||||||
"@types/sinonjs__fake-timers": "8.1.2",
|
"@types/sinonjs__fake-timers": "8.1.2",
|
||||||
"@types/speakeasy": "2.0.7",
|
"@types/speakeasy": "2.0.7",
|
||||||
"@types/throttle-debounce": "2.1.0",
|
|
||||||
"@types/tinycolor2": "1.4.3",
|
"@types/tinycolor2": "1.4.3",
|
||||||
"@types/tmp": "0.2.3",
|
"@types/tmp": "0.2.3",
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
"@types/web-push": "3.3.2",
|
"@types/web-push": "3.3.2",
|
||||||
"@types/websocket": "1.0.5",
|
"@types/websocket": "1.0.5",
|
||||||
"@types/ws": "8.5.3",
|
"@types/ws": "8.5.3",
|
||||||
"@typescript-eslint/eslint-plugin": "5.17.0",
|
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||||
"@typescript-eslint/parser": "5.17.0",
|
"@typescript-eslint/parser": "5.18.0",
|
||||||
"@bull-board/koa": "3.10.2",
|
"@bull-board/koa": "3.10.3",
|
||||||
"abort-controller": "3.0.0",
|
"abort-controller": "3.0.0",
|
||||||
"ajv": "8.11.0",
|
"ajv": "8.11.0",
|
||||||
"archiver": "5.3.0",
|
"archiver": "5.3.0",
|
||||||
"autobind-decorator": "2.4.0",
|
"autobind-decorator": "2.4.0",
|
||||||
"autwh": "0.1.0",
|
"autwh": "0.1.0",
|
||||||
"aws-sdk": "2.1105.0",
|
"aws-sdk": "2.1111.0",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "1.1.5",
|
"blurhash": "1.1.5",
|
||||||
"broadcast-channel": "4.10.0",
|
"broadcast-channel": "4.10.0",
|
||||||
@ -89,8 +88,8 @@
|
|||||||
"date-fns": "2.28.0",
|
"date-fns": "2.28.0",
|
||||||
"deep-email-validator": "0.1.21",
|
"deep-email-validator": "0.1.21",
|
||||||
"escape-regexp": "0.0.1",
|
"escape-regexp": "0.0.1",
|
||||||
"eslint": "8.12.0",
|
"eslint": "8.13.0",
|
||||||
"eslint-plugin-import": "2.25.4",
|
"eslint-plugin-import": "2.26.0",
|
||||||
"feed": "4.2.2",
|
"feed": "4.2.2",
|
||||||
"file-type": "17.1.1",
|
"file-type": "17.1.1",
|
||||||
"fluent-ffmpeg": "2.1.2",
|
"fluent-ffmpeg": "2.1.2",
|
||||||
@ -145,7 +144,7 @@
|
|||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
"sanitize-html": "2.7.0",
|
"sanitize-html": "2.7.0",
|
||||||
"semver": "7.3.5",
|
"semver": "7.3.6",
|
||||||
"sharp": "0.30.3",
|
"sharp": "0.30.3",
|
||||||
"speakeasy": "2.0.0",
|
"speakeasy": "2.0.0",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
@ -154,7 +153,6 @@
|
|||||||
"summaly": "2.5.0",
|
"summaly": "2.5.0",
|
||||||
"syslog-pro": "1.0.0",
|
"syslog-pro": "1.0.0",
|
||||||
"systeminformation": "5.11.9",
|
"systeminformation": "5.11.9",
|
||||||
"throttle-debounce": "3.0.1",
|
|
||||||
"tinycolor2": "1.4.2",
|
"tinycolor2": "1.4.2",
|
||||||
"tmp": "0.2.1",
|
"tmp": "0.2.1",
|
||||||
"ts-loader": "9.2.8",
|
"ts-loader": "9.2.8",
|
||||||
@ -162,7 +160,7 @@
|
|||||||
"tsc-alias": "1.4.1",
|
"tsc-alias": "1.4.1",
|
||||||
"tsconfig-paths": "3.14.1",
|
"tsconfig-paths": "3.14.1",
|
||||||
"twemoji-parser": "14.0.0",
|
"twemoji-parser": "14.0.0",
|
||||||
"typeorm": "0.3.4",
|
"typeorm": "0.3.5",
|
||||||
"typescript": "4.6.3",
|
"typescript": "4.6.3",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.3.0",
|
||||||
"unzipper": "0.10.11",
|
"unzipper": "0.10.11",
|
||||||
@ -173,7 +171,7 @@
|
|||||||
"xev": "2.0.1"
|
"xev": "2.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@redocly/openapi-core": "1.0.0-beta.91",
|
"@redocly/openapi-core": "1.0.0-beta.93",
|
||||||
"@types/fluent-ffmpeg": "2.1.20",
|
"@types/fluent-ffmpeg": "2.1.20",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"execa": "6.1.0"
|
"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_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
|
||||||
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
|
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
|
||||||
|
@ -209,7 +209,11 @@ export const db = new DataSource({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export async function initDb() {
|
export async function initDb() {
|
||||||
|
if (db.isInitialized) {
|
||||||
|
// nop
|
||||||
|
} else {
|
||||||
await db.connect();
|
await db.connect();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resetDb() {
|
export async function resetDb() {
|
||||||
|
@ -42,7 +42,8 @@ async function getCaptchaResponse(url: string, secret: string, response: string)
|
|||||||
headers: {
|
headers: {
|
||||||
'User-Agent': config.userAgent,
|
'User-Agent': config.userAgent,
|
||||||
},
|
},
|
||||||
timeout: 10 * 1000,
|
// TODO
|
||||||
|
//timeout: 10 * 1000,
|
||||||
agent: getAgentByUrl,
|
agent: getAgentByUrl,
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
throw `${e.message || e}`;
|
throw `${e.message || e}`;
|
||||||
|
@ -120,9 +120,9 @@ export const httpsAgent = config.proxy
|
|||||||
*/
|
*/
|
||||||
export function getAgentByUrl(url: URL, bypassProxy = false) {
|
export function getAgentByUrl(url: URL, bypassProxy = false) {
|
||||||
if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) {
|
if (bypassProxy || (config.proxyBypassHosts || []).includes(url.hostname)) {
|
||||||
return url.protocol == 'http:' ? _http : _https;
|
return url.protocol === 'http:' ? _http : _https;
|
||||||
} else {
|
} 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})`;
|
summary += ` (📎${note.files!.length})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
import { Entity, Column, Index, OneToOne, JoinColumn, PrimaryColumn } from 'typeorm';
|
||||||
import { DriveFile } from './drive-file.js';
|
|
||||||
import { id } from '../id.js';
|
import { id } from '../id.js';
|
||||||
|
import { DriveFile } from './drive-file.js';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Index(['usernameLower', 'host'], { unique: true })
|
@Index(['usernameLower', 'host'], { unique: true })
|
||||||
@ -207,7 +207,7 @@ export class User {
|
|||||||
|
|
||||||
@Column('boolean', {
|
@Column('boolean', {
|
||||||
default: false,
|
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;
|
public showTimelineReplies: boolean;
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { EntityRepository, Repository, In, Not } from 'typeorm';
|
import { EntityRepository, Repository, In, Not } from 'typeorm';
|
||||||
import Ajv from 'ajv';
|
import Ajv from 'ajv';
|
||||||
import { User, ILocalUser, IRemoteUser } from '@/models/entities/user.js';
|
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 config from '@/config/index.js';
|
||||||
import { Packed } from '@/misc/schema.js';
|
import { Packed } from '@/misc/schema.js';
|
||||||
import { awaitAll, Promiseable } from '@/prelude/await-all.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 { getAntennas } from '@/misc/antenna-cache.js';
|
||||||
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js';
|
||||||
import { Cache } from '@/misc/cache.js';
|
import { Cache } from '@/misc/cache.js';
|
||||||
import { Instance } from '../entities/instance.js';
|
|
||||||
import { db } from '@/db/postgre.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);
|
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 joinings = await UserGroupJoinings.findBy({ userId: userId });
|
||||||
|
|
||||||
const groupQs = Promise.all(joinings.map(j => MessagingMessages.createQueryBuilder('message')
|
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('message.userId != :userId', { userId: userId })
|
||||||
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
|
.andWhere('NOT (:userId = ANY(message.reads))', { userId: userId })
|
||||||
.andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
.andWhere('message.createdAt > :joinedAt', { joinedAt: j.createdAt }) // 自分が加入する前の会話については、未読扱いしない
|
||||||
@ -204,8 +204,18 @@ export const UserRepository = db.getRepository(User).extend({
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
getAvatarUrl(user: User): string {
|
async getAvatarUrl(user: User): Promise<string> {
|
||||||
// TODO: avatarIdがあるがavatarがない(JOINされてない)場合のハンドリング
|
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) {
|
if (user.avatar) {
|
||||||
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
|
return DriveFiles.getPublicUrl(user.avatar, true) || this.getIdenticonUrl(user.id);
|
||||||
} else {
|
} else {
|
||||||
@ -223,7 +233,7 @@ export const UserRepository = db.getRepository(User).extend({
|
|||||||
options?: {
|
options?: {
|
||||||
detail?: D,
|
detail?: D,
|
||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
}
|
},
|
||||||
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
): Promise<IsMeAndIsUserDetailed<ExpectsMe, D>> {
|
||||||
const opts = Object.assign({
|
const opts = Object.assign({
|
||||||
detail: false,
|
detail: false,
|
||||||
@ -274,7 +284,7 @@ export const UserRepository = db.getRepository(User).extend({
|
|||||||
name: user.name,
|
name: user.name,
|
||||||
username: user.username,
|
username: user.username,
|
||||||
host: user.host,
|
host: user.host,
|
||||||
avatarUrl: this.getAvatarUrl(user),
|
avatarUrl: this.getAvatarUrlSync(user),
|
||||||
avatarBlurhash: user.avatar?.blurhash || null,
|
avatarBlurhash: user.avatar?.blurhash || null,
|
||||||
avatarColor: null, // 後方互換性のため
|
avatarColor: null, // 後方互換性のため
|
||||||
isAdmin: user.isAdmin || falsy,
|
isAdmin: user.isAdmin || falsy,
|
||||||
@ -283,7 +293,7 @@ export const UserRepository = db.getRepository(User).extend({
|
|||||||
isCat: user.isCat || falsy,
|
isCat: user.isCat || falsy,
|
||||||
instance: user.host ? userInstanceCache.fetch(user.host,
|
instance: user.host ? userInstanceCache.fetch(user.host,
|
||||||
() => Instances.findOneBy({ host: user.host! }),
|
() => Instances.findOneBy({ host: user.host! }),
|
||||||
v => v != null
|
v => v != null,
|
||||||
).then(instance => instance ? {
|
).then(instance => instance ? {
|
||||||
name: instance.name,
|
name: instance.name,
|
||||||
softwareName: instance.softwareName,
|
softwareName: instance.softwareName,
|
||||||
@ -403,7 +413,7 @@ export const UserRepository = db.getRepository(User).extend({
|
|||||||
options?: {
|
options?: {
|
||||||
detail?: D,
|
detail?: D,
|
||||||
includeSecrets?: boolean,
|
includeSecrets?: boolean,
|
||||||
}
|
},
|
||||||
): Promise<IsUserDetailed<D>[]> {
|
): Promise<IsUserDetailed<D>[]> {
|
||||||
return Promise.all(users.map(u => this.pack(u, me, options)));
|
return Promise.all(users.map(u => this.pack(u, me, options)));
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@ export const packedEmojiSchema = {
|
|||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -21,6 +21,7 @@ export const packedUserLiteSchema = {
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
nullable: true, optional: false,
|
nullable: true, optional: false,
|
||||||
example: 'misskey.example.com',
|
example: 'misskey.example.com',
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
avatarUrl: {
|
avatarUrl: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import httpSignature from 'http-signature';
|
import httpSignature from 'http-signature';
|
||||||
|
import { v4 as uuid } from 'uuid';
|
||||||
|
|
||||||
import config from '@/config/index.js';
|
import config from '@/config/index.js';
|
||||||
import { envOption } from '../env.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 { systemQueue, dbQueue, deliverQueue, inboxQueue, objectStorageQueue, endedPollNotificationQueue, webhookDeliverQueue } from './queues.js';
|
||||||
import { ThinUser } from './types.js';
|
import { ThinUser } from './types.js';
|
||||||
import { IActivity } from '@/remote/activitypub/type.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 {
|
function renderError(e: Error): any {
|
||||||
return {
|
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 = {
|
const data = {
|
||||||
|
type,
|
||||||
content,
|
content,
|
||||||
webhookId: webhook.id,
|
webhookId: webhook.id,
|
||||||
|
userId: webhook.userId,
|
||||||
to: webhook.url,
|
to: webhook.url,
|
||||||
secret: webhook.secret,
|
secret: webhook.secret,
|
||||||
|
createdAt: Date.now(),
|
||||||
|
eventId: uuid(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return webhookDeliverQueue.add(data, {
|
return webhookDeliverQueue.add(data, {
|
||||||
|
@ -8,13 +8,9 @@ import config from '@/config/index.js';
|
|||||||
|
|
||||||
const logger = new Logger('webhook');
|
const logger = new Logger('webhook');
|
||||||
|
|
||||||
let latest: string | null = null;
|
|
||||||
|
|
||||||
export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
export default async (job: Bull.Job<WebhookDeliverJobData>) => {
|
||||||
try {
|
try {
|
||||||
if (latest !== (latest = JSON.stringify(job.data.content, null, 2))) {
|
logger.debug(`delivering ${job.data.webhookId}`);
|
||||||
logger.debug(`delivering ${latest}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getResponse({
|
const res = await getResponse({
|
||||||
url: job.data.to,
|
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-Id': job.data.webhookId,
|
||||||
'X-Misskey-Hook-Secret': job.data.secret,
|
'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 }, {
|
Webhooks.update({ id: job.data.webhookId }, {
|
||||||
|
@ -48,10 +48,14 @@ export type EndedPollNotificationJobData = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type WebhookDeliverJobData = {
|
export type WebhookDeliverJobData = {
|
||||||
|
type: string;
|
||||||
content: unknown;
|
content: unknown;
|
||||||
webhookId: Webhook['id'];
|
webhookId: Webhook['id'];
|
||||||
|
userId: User['id'];
|
||||||
to: string;
|
to: string;
|
||||||
secret: string;
|
secret: string;
|
||||||
|
createdAt: number;
|
||||||
|
eventId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ThinUser = {
|
export type ThinUser = {
|
||||||
|
@ -95,7 +95,7 @@ function genSigningString(request: Request, includeHeaders: string[]) {
|
|||||||
|
|
||||||
function lcObjectKey(src: Record<string, string>) {
|
function lcObjectKey(src: Record<string, string>) {
|
||||||
const dst: 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;
|
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
|
// followers recipes have already been processed
|
||||||
isDirect(recipe)
|
isDirect(recipe)
|
||||||
// check that shared inbox has not been added yet
|
// check that shared inbox has not been added yet
|
||||||
&& !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox))
|
&& !(recipe.to.sharedInbox && inboxes.has(recipe.to.sharedInbox))
|
||||||
// check that they actually have an inbox
|
// check that they actually have an inbox
|
||||||
&& recipe.to.inbox
|
&& recipe.to.inbox != null,
|
||||||
})
|
)
|
||||||
.forEach(recipe => inboxes.add(recipe.to.inbox));
|
.forEach(recipe => inboxes.add(recipe.to.inbox!));
|
||||||
|
|
||||||
// deliver
|
// deliver
|
||||||
for (const inbox of inboxes) {
|
for (const inbox of inboxes) {
|
||||||
|
@ -18,7 +18,7 @@ export const performReadActivity = async (actor: CacheableRemoteUser, activity:
|
|||||||
return `skip: message not found`;
|
return `skip: message not found`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actor.id != message.recipientId) {
|
if (actor.id !== message.recipientId) {
|
||||||
return `skip: actor is not a message recipient`;
|
return `skip: actor is not a message recipient`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import unfollow from '@/services/following/delete.js';
|
import unfollow from '@/services/following/delete.js';
|
||||||
import cancelRequest from '@/services/following/requests/cancel.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 { CacheableRemoteUser } from '@/models/entities/user.js';
|
||||||
import { Followings } from '@/models/index.js';
|
import { Followings } from '@/models/index.js';
|
||||||
import DbResolver from '../../db-resolver.js';
|
import DbResolver from '../../db-resolver.js';
|
||||||
|
@ -113,7 +113,8 @@ export class LdSignature {
|
|||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/ld+json, application/json',
|
Accept: 'application/ld+json, application/json',
|
||||||
},
|
},
|
||||||
timeout: this.loderTimeout,
|
// TODO
|
||||||
|
//timeout: this.loderTimeout,
|
||||||
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
|
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
|
@ -69,7 +69,7 @@ export async function updateQuestion(value: any) {
|
|||||||
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
const oldCount = poll.votes[poll.choices.indexOf(choice)];
|
||||||
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
|
||||||
|
|
||||||
if (oldCount != newCount) {
|
if (oldCount !== newCount) {
|
||||||
changed = true;
|
changed = true;
|
||||||
poll.votes[poll.choices.indexOf(choice)] = newCount;
|
poll.votes[poll.choices.indexOf(choice)] = newCount;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ type IWebFinger = {
|
|||||||
export default async function(query: string): Promise<IWebFinger> {
|
export default async function(query: string): Promise<IWebFinger> {
|
||||||
const url = genUrl(query);
|
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) {
|
function genUrl(query: string) {
|
||||||
|
@ -121,14 +121,14 @@ export function verifyLogin({
|
|||||||
signature: Buffer,
|
signature: Buffer,
|
||||||
challenge: string
|
challenge: string
|
||||||
}) {
|
}) {
|
||||||
if (clientData.type != 'webauthn.get') {
|
if (clientData.type !== 'webauthn.get') {
|
||||||
throw new Error('type is not 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');
|
throw new Error('challenge mismatch');
|
||||||
}
|
}
|
||||||
if (clientData.origin != config.scheme + '://' + config.host) {
|
if (clientData.origin !== config.scheme + '://' + config.host) {
|
||||||
throw new Error('origin mismatch');
|
throw new Error('origin mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,11 +148,11 @@ export const procedures = {
|
|||||||
verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
|
verify({ publicKey }: {publicKey: Map<number, Buffer>}) {
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ export const procedures = {
|
|||||||
rpIdHash: Buffer,
|
rpIdHash: Buffer,
|
||||||
credentialId: Buffer,
|
credentialId: Buffer,
|
||||||
}) {
|
}) {
|
||||||
if (attStmt.alg != -7) {
|
if (attStmt.alg !== -7) {
|
||||||
throw new Error('alg mismatch');
|
throw new Error('alg mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,11 +196,11 @@ export const procedures = {
|
|||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,7 +263,7 @@ export const procedures = {
|
|||||||
.map((key: any) => PEMString(key))
|
.map((key: any) => PEMString(key))
|
||||||
.concat([GSR2]);
|
.concat([GSR2]);
|
||||||
|
|
||||||
if (getCertSubject(certificateChain[0]).CN != 'attest.android.com') {
|
if (getCertSubject(certificateChain[0]).CN !== 'attest.android.com') {
|
||||||
throw new Error('invalid common name');
|
throw new Error('invalid common name');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,11 +283,11 @@ export const procedures = {
|
|||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
throw new Error('invalid or no -3 key given');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,11 +332,11 @@ export const procedures = {
|
|||||||
|
|
||||||
const negTwo = publicKey.get(-2);
|
const negTwo = publicKey.get(-2);
|
||||||
|
|
||||||
if (!negTwo || negTwo.length != 32) {
|
if (!negTwo || negTwo.length !== 32) {
|
||||||
throw new Error('invalid or no -2 key given');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree = publicKey.get(-3);
|
const negThree = publicKey.get(-3);
|
||||||
if (!negThree || negThree.length != 32) {
|
if (!negThree || negThree.length !== 32) {
|
||||||
throw new Error('invalid or no -3 key given');
|
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
|
// 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');
|
throw new Error('ECDAA-Verify is not supported');
|
||||||
} else {
|
} 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');
|
throw new Error('self attestation is not supported');
|
||||||
}
|
}
|
||||||
@ -377,7 +377,7 @@ export const procedures = {
|
|||||||
credentialId: Buffer
|
credentialId: Buffer
|
||||||
}) {
|
}) {
|
||||||
const x5c: Buffer[] = attStmt.x5c;
|
const x5c: Buffer[] = attStmt.x5c;
|
||||||
if (x5c.length != 1) {
|
if (x5c.length !== 1) {
|
||||||
throw new Error('x5c length does not match expectation');
|
throw new Error('x5c length does not match expectation');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,11 +387,11 @@ export const procedures = {
|
|||||||
|
|
||||||
const negTwo: Buffer = publicKey.get(-2);
|
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');
|
throw new Error('invalid or no -2 key given');
|
||||||
}
|
}
|
||||||
const negThree: Buffer = publicKey.get(-3);
|
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');
|
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) {
|
export default function(ctx: Koa.Context, user: ILocalUser, redirect = false) {
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
//#region Cookie
|
//#region Cookie
|
||||||
ctx.cookies.set('igi', user.token, {
|
ctx.cookies.set('igi', user.token!, {
|
||||||
path: '/',
|
path: '/',
|
||||||
// SEE: https://github.com/koajs/koa/issues/974
|
// 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
|
// 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' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
|
type: { type: 'string', nullable: true, pattern: /^[a-zA-Z0-9\/\-*]+$/.toString().slice(1, -1) },
|
||||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
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: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -40,6 +40,7 @@ export const meta = {
|
|||||||
userHost: {
|
userHost: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
md5: {
|
md5: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -151,11 +152,20 @@ export const meta = {
|
|||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['fileId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['url'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -40,6 +40,7 @@ export const meta = {
|
|||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
@ -54,7 +55,12 @@ export const paramDef = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
query: { type: 'string', nullable: true, default: null },
|
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 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
@ -38,8 +38,9 @@ export const meta = {
|
|||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
},
|
},
|
||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'null',
|
||||||
optional: false, nullable: true,
|
optional: false,
|
||||||
|
description: 'The local host is represented with `null`. The field exists for compatibility with other API endpoints that return files.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -17,7 +17,11 @@ export const paramDef = {
|
|||||||
ids: { type: 'array', items: {
|
ids: { type: 'array', items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
category: { type: 'string', nullable: true },
|
category: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'Use `null` to reset the category.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: ['ids'],
|
required: ['ids'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -23,7 +23,11 @@ export const paramDef = {
|
|||||||
properties: {
|
properties: {
|
||||||
id: { type: 'string', format: 'misskey:id' },
|
id: { type: 'string', format: 'misskey:id' },
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
category: { type: 'string', nullable: true },
|
category: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'Use `null` to reset the category.',
|
||||||
|
},
|
||||||
aliases: { type: 'array', items: {
|
aliases: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
@ -26,8 +26,13 @@ export const paramDef = {
|
|||||||
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
|
sort: { type: 'string', enum: ['+follower', '-follower', '+createdAt', '-createdAt', '+updatedAt', '-updatedAt'] },
|
||||||
state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
|
state: { type: 'string', enum: ['all', 'available', 'admin', 'moderator', 'adminOrModerator', 'silenced', 'suspended'], default: "all" },
|
||||||
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
origin: { type: 'string', enum: ['combined', 'local', 'remote'], default: "local" },
|
||||||
username: { type: 'string', default: null },
|
username: { type: 'string', nullable: true, default: null },
|
||||||
hostname: { type: 'string', default: null },
|
hostname: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
required: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -397,12 +397,14 @@ export default define(meta, paramDef, async (ps, me) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await db.transaction(async transactionalEntityManager => {
|
await db.transaction(async transactionalEntityManager => {
|
||||||
const meta = await transactionalEntityManager.findOne(Meta, {
|
const metas = await transactionalEntityManager.find(Meta, {
|
||||||
order: {
|
order: {
|
||||||
id: 'DESC',
|
id: 'DESC',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const meta = metas[0];
|
||||||
|
|
||||||
if (meta) {
|
if (meta) {
|
||||||
await transactionalEntityManager.update(Meta, meta.id, set);
|
await transactionalEntityManager.update(Meta, meta.id, set);
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,13 +57,9 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
throw new ApiError(meta.errors.noSuchAntenna);
|
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'),
|
const query = makePaginationQuery(Notes.createQueryBuilder('note'),
|
||||||
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
|
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')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||||
.leftJoinAndSelect('user.banner', 'banner')
|
.leftJoinAndSelect('user.banner', 'banner')
|
||||||
@ -75,7 +71,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||||
.setParameters(antennaQuery.getParameters());
|
.andWhere('antennaNote.antennaId = :antennaId', { antennaId: antenna.id });
|
||||||
|
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
generateMutedUserQuery(query, user);
|
generateMutedUserQuery(query, user);
|
||||||
|
@ -20,7 +20,7 @@ export const paramDef = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
name: { type: 'string', minLength: 1, maxLength: 100 },
|
name: { type: 'string', minLength: 1, maxLength: 100 },
|
||||||
isPublic: { type: 'boolean' },
|
isPublic: { type: 'boolean', default: false },
|
||||||
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
|
description: { type: 'string', nullable: true, minLength: 1, maxLength: 2048 },
|
||||||
},
|
},
|
||||||
required: ['name'],
|
required: ['name'],
|
||||||
|
@ -57,12 +57,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
throw new ApiError(meta.errors.noSuchClip);
|
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)
|
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')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||||
.leftJoinAndSelect('user.banner', 'banner')
|
.leftJoinAndSelect('user.banner', 'banner')
|
||||||
@ -74,7 +70,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||||
.setParameters(clipQuery.getParameters());
|
.andWhere('clipNote.clipId = :clipId', { clipId: clip.id });
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
|
@ -48,7 +48,6 @@ export const paramDef = {
|
|||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
// @ts-ignore
|
|
||||||
export default define(meta, paramDef, async (ps, user, _, file, cleanup) => {
|
export default define(meta, paramDef, async (ps, user, _, file, cleanup) => {
|
||||||
// Get 'name' parameter
|
// Get 'name' parameter
|
||||||
let name = ps.name || file.originalname;
|
let name = ps.name || file.originalname;
|
||||||
|
@ -28,22 +28,25 @@ export const meta = {
|
|||||||
code: 'ACCESS_DENIED',
|
code: 'ACCESS_DENIED',
|
||||||
id: '25b73c73-68b1-41d0-bad1-381cfdf6579f',
|
id: '25b73c73-68b1-41d0-bad1-381cfdf6579f',
|
||||||
},
|
},
|
||||||
|
|
||||||
fileIdOrUrlRequired: {
|
|
||||||
message: 'fileId or url required.',
|
|
||||||
code: 'INVALID_PARAM',
|
|
||||||
id: '89674805-722c-440c-8d88-5641830dc3e4',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
fileId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['fileId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
url: { type: 'string' },
|
url: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['url'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
@ -62,8 +65,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
thumbnailUrl: ps.url,
|
thumbnailUrl: ps.url,
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
throw new ApiError(meta.errors.fileIdOrUrlRequired);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
|
@ -22,7 +22,7 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
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 },
|
blocked: { type: 'boolean', nullable: true },
|
||||||
notResponding: { type: 'boolean', nullable: true },
|
notResponding: { type: 'boolean', nullable: true },
|
||||||
suspended: { 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);
|
const clientData = JSON.parse(ps.clientDataJSON);
|
||||||
|
|
||||||
if (clientData.type != 'webauthn.create') {
|
if (clientData.type !== 'webauthn.create') {
|
||||||
throw new Error('not a creation attestation');
|
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');
|
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 credentialId = authData.slice(55, 55 + credentialIdLength);
|
||||||
const publicKeyData = authData.slice(55 + credentialIdLength);
|
const publicKeyData = authData.slice(55 + credentialIdLength);
|
||||||
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
const publicKey: Map<number, any> = await cborDecodeFirst(publicKeyData);
|
||||||
if (publicKey.get(3) != -7) {
|
if (publicKey.get(3) !== -7) {
|
||||||
throw new Error('alg mismatch');
|
throw new Error('alg mismatch');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
take: ps.limit,
|
take: ps.limit,
|
||||||
skip: ps.offset,
|
skip: ps.offset,
|
||||||
order: {
|
order: {
|
||||||
id: ps.sort == 'asc' ? 1 : -1,
|
id: ps.sort === 'asc' ? 1 : -1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -47,14 +47,25 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
groupId: { type: 'string', format: 'misskey:id' },
|
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
markAsRead: { type: 'boolean', default: true },
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// 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, {
|
return await Promise.all(messages.map(message => MessagingMessages.pack(message, user, {
|
||||||
populateGroup: false,
|
populateGroup: false,
|
||||||
})));
|
})));
|
||||||
} else {
|
|
||||||
throw new Error();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -67,12 +67,23 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
groupId: { type: 'string', format: 'misskey:id' },
|
|
||||||
text: { type: 'string', nullable: true, maxLength: 3000 },
|
text: { type: 'string', nullable: true, maxLength: 3000 },
|
||||||
fileId: { type: 'string', format: 'misskey:id' },
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -169,6 +169,7 @@ export const meta = {
|
|||||||
host: {
|
host: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
optional: false, nullable: true,
|
optional: false, nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
},
|
},
|
||||||
url: {
|
url: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
|
@ -38,7 +38,11 @@ export const paramDef = {
|
|||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
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'],
|
required: ['userId'],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -19,7 +19,7 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
local: { type: 'boolean' },
|
local: { type: 'boolean', default: false },
|
||||||
reply: { type: 'boolean' },
|
reply: { type: 'boolean' },
|
||||||
renote: { type: 'boolean' },
|
renote: { type: 'boolean' },
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: { type: 'boolean' },
|
||||||
@ -52,19 +52,19 @@ export default define(meta, paramDef, async (ps) => {
|
|||||||
query.andWhere('note.userHost IS NULL');
|
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');
|
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');
|
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 = '{}'`);
|
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');
|
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);
|
conversation.push(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conversation.length == ps.limit) {
|
if (conversation.length === ps.limit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { Note } from '@/models/entities/note.js';
|
|||||||
import { noteVisibilities } from '../../../../types.js';
|
import { noteVisibilities } from '../../../../types.js';
|
||||||
import { Channel } from '@/models/entities/channel.js';
|
import { Channel } from '@/models/entities/channel.js';
|
||||||
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
import { MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||||
|
import { In } from 'typeorm';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
tags: ['notes'],
|
tags: ['notes'],
|
||||||
@ -59,12 +60,6 @@ export const meta = {
|
|||||||
id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15',
|
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: {
|
cannotCreateAlreadyExpiredPoll: {
|
||||||
message: 'Poll is already expired.',
|
message: 'Poll is already expired.',
|
||||||
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
|
code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL',
|
||||||
@ -92,29 +87,41 @@ export const paramDef = {
|
|||||||
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
visibleUserIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
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 },
|
cw: { type: 'string', nullable: true, maxLength: 100 },
|
||||||
localOnly: { type: 'boolean', default: false },
|
localOnly: { type: 'boolean', default: false },
|
||||||
noExtractMentions: { type: 'boolean', default: false },
|
noExtractMentions: { type: 'boolean', default: false },
|
||||||
noExtractHashtags: { type: 'boolean', default: false },
|
noExtractHashtags: { type: 'boolean', default: false },
|
||||||
noExtractEmojis: { type: 'boolean', default: false },
|
noExtractEmojis: { type: 'boolean', default: false },
|
||||||
fileIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
fileIds: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'array',
|
||||||
} },
|
uniqueItems: true,
|
||||||
mediaIds: { type: 'array', uniqueItems: true, minItems: 1, maxItems: 16, items: {
|
minItems: 1,
|
||||||
type: 'string', format: 'misskey:id',
|
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 },
|
replyId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
renoteId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
channelId: { type: 'string', format: 'misskey:id', nullable: true },
|
||||||
poll: {
|
poll: {
|
||||||
type: 'object', nullable: true,
|
type: 'object',
|
||||||
|
nullable: true,
|
||||||
properties: {
|
properties: {
|
||||||
choices: {
|
choices: {
|
||||||
type: 'array', uniqueItems: true, minItems: 2, maxItems: 10,
|
type: 'array',
|
||||||
items: {
|
uniqueItems: true,
|
||||||
type: 'string', minLength: 1, maxLength: 50,
|
minItems: 2,
|
||||||
},
|
maxItems: 10,
|
||||||
|
items: { type: 'string', minLength: 1, maxLength: 50 },
|
||||||
},
|
},
|
||||||
multiple: { type: 'boolean', default: false },
|
multiple: { type: 'boolean', default: false },
|
||||||
expiresAt: { type: 'integer', nullable: true },
|
expiresAt: { type: 'integer', nullable: true },
|
||||||
@ -123,26 +130,52 @@ export const paramDef = {
|
|||||||
required: ['choices'],
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
export default define(meta, paramDef, async (ps, user) => {
|
||||||
let visibleUsers: User[] = [];
|
let visibleUsers: User[] = [];
|
||||||
if (ps.visibleUserIds) {
|
if (ps.visibleUserIds) {
|
||||||
visibleUsers = (await Promise.all(ps.visibleUserIds.map(id => Users.findOneBy({ id }))))
|
visibleUsers = await Users.findBy({
|
||||||
.filter(x => x != null) as User[];
|
id: In(ps.visibleUserIds),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let files: DriveFile[] = [];
|
let files: DriveFile[] = [];
|
||||||
const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
|
const fileIds = ps.fileIds != null ? ps.fileIds : ps.mediaIds != null ? ps.mediaIds : null;
|
||||||
if (fileIds != null) {
|
if (fileIds != null) {
|
||||||
files = (await Promise.all(fileIds.map(fileId =>
|
files = await DriveFiles.findBy({
|
||||||
DriveFiles.findOneBy({
|
|
||||||
id: fileId,
|
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
})
|
id: In(fileIds),
|
||||||
))).filter(file => file != null) as DriveFile[];
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let renote: Note | null;
|
let renote: Note | null;
|
||||||
@ -152,7 +185,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
|
|
||||||
if (renote == null) {
|
if (renote == null) {
|
||||||
throw new ApiError(meta.errors.noSuchRenoteTarget);
|
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);
|
throw new ApiError(meta.errors.cannotReRenote);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,10 +208,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
|
|
||||||
if (reply == null) {
|
if (reply == null) {
|
||||||
throw new ApiError(meta.errors.noSuchReplyTarget);
|
throw new ApiError(meta.errors.noSuchReplyTarget);
|
||||||
}
|
} else if (reply.renoteId && !reply.text && !reply.fileIds && !renote.poll) {
|
||||||
|
|
||||||
// 返信対象が引用でないRenoteだったらエラー
|
|
||||||
if (reply.renoteId && !reply.text && !reply.fileIds) {
|
|
||||||
throw new ApiError(meta.errors.cannotReplyToPureRenote);
|
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;
|
let channel: Channel | undefined;
|
||||||
if (ps.channelId != null) {
|
if (ps.channelId != null) {
|
||||||
channel = await Channels.findOneBy({ id: ps.channelId });
|
channel = await Channels.findOneBy({ id: ps.channelId });
|
||||||
|
@ -35,7 +35,11 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
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 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
|
@ -48,7 +48,11 @@ export const paramDef = {
|
|||||||
includeMyRenotes: { type: 'boolean', default: true },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { 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: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -37,7 +37,11 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
withFiles: { type: 'boolean' },
|
withFiles: {
|
||||||
|
type: 'boolean',
|
||||||
|
default: false,
|
||||||
|
description: 'Only show notes that have attached files.',
|
||||||
|
},
|
||||||
fileType: { type: 'array', items: {
|
fileType: { type: 'array', items: {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
} },
|
} },
|
||||||
|
@ -110,7 +110,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
|
|
||||||
if (exist.length) {
|
if (exist.length) {
|
||||||
if (poll.multiple) {
|
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);
|
throw new ApiError(meta.errors.alreadyVoted);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,21 +25,44 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
tag: { type: 'string' },
|
|
||||||
query: { type: 'array', items: {
|
|
||||||
type: 'array', items: {
|
|
||||||
type: 'string',
|
|
||||||
},
|
|
||||||
} },
|
|
||||||
reply: { type: 'boolean', nullable: true, default: null },
|
reply: { type: 'boolean', nullable: true, default: null },
|
||||||
renote: { 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 },
|
poll: { type: 'boolean', nullable: true, default: null },
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -35,7 +35,11 @@ export const paramDef = {
|
|||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
offset: { type: 'integer', default: 0 },
|
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 },
|
userId: { type: 'string', format: 'misskey:id', nullable: true, default: null },
|
||||||
channelId: { 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 },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { 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: [],
|
required: [],
|
||||||
} as const;
|
} as const;
|
||||||
|
@ -75,7 +75,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
Accept: 'application/json, */*',
|
Accept: 'application/json, */*',
|
||||||
},
|
},
|
||||||
body: params,
|
body: params,
|
||||||
timeout: 10000,
|
// TODO
|
||||||
|
//timeout: 10000,
|
||||||
agent: getAgentByUrl,
|
agent: getAgentByUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -42,7 +42,11 @@ export const paramDef = {
|
|||||||
includeMyRenotes: { type: 'boolean', default: true },
|
includeMyRenotes: { type: 'boolean', default: true },
|
||||||
includeRenotedMyNotes: { type: 'boolean', default: true },
|
includeRenotedMyNotes: { type: 'boolean', default: true },
|
||||||
includeLocalRenotes: { 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'],
|
required: ['listId'],
|
||||||
} as const;
|
} as const;
|
||||||
@ -59,12 +63,8 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Construct query
|
//#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)
|
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')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('user.avatar', 'avatar')
|
.leftJoinAndSelect('user.avatar', 'avatar')
|
||||||
.leftJoinAndSelect('user.banner', 'banner')
|
.leftJoinAndSelect('user.banner', 'banner')
|
||||||
@ -76,7 +76,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
|||||||
.leftJoinAndSelect('renote.user', 'renoteUser')
|
.leftJoinAndSelect('renote.user', 'renoteUser')
|
||||||
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
|
||||||
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner')
|
||||||
.setParameters(listQuery.getParameters());
|
.andWhere('userListJoining.userListId = :userListId', { userListId: list.id });
|
||||||
|
|
||||||
generateVisibilityQuery(query, user);
|
generateVisibilityQuery(query, user);
|
||||||
|
|
||||||
|
@ -26,12 +26,21 @@ export const meta = {
|
|||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
pageId: { type: 'string', format: 'misskey:id' },
|
pageId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['pageId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
name: { type: 'string' },
|
name: { type: 'string' },
|
||||||
username: { type: 'string' },
|
username: { type: 'string' },
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['name', 'username'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -38,14 +38,29 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -38,14 +38,29 @@ export const meta = {
|
|||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
sinceId: { type: 'string', format: 'misskey:id' },
|
sinceId: { type: 'string', format: 'misskey:id' },
|
||||||
untilId: { type: 'string', format: 'misskey:id' },
|
untilId: { type: 'string', format: 'misskey:id' },
|
||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
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;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -28,7 +28,10 @@ export const paramDef = {
|
|||||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||||
detail: { type: 'boolean', default: true },
|
detail: { type: 'boolean', default: true },
|
||||||
},
|
},
|
||||||
required: [],
|
anyOf: [
|
||||||
|
{ required: ['username'] },
|
||||||
|
{ required: ['host'] },
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// TODO: avatar,bannerをJOINしたいけどエラーになる
|
// TODO: avatar,bannerをJOINしたいけどエラーになる
|
||||||
|
@ -23,9 +23,9 @@ export const meta = {
|
|||||||
items: {
|
items: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
ref: 'UserDetailed',
|
ref: 'UserDetailed',
|
||||||
}
|
|
||||||
},
|
},
|
||||||
]
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
@ -46,15 +46,33 @@ export const meta = {
|
|||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
properties: {
|
properties: {
|
||||||
userId: { type: 'string', format: 'misskey:id' },
|
userId: { type: 'string', format: 'misskey:id' },
|
||||||
|
},
|
||||||
|
required: ['userId'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
userIds: { type: 'array', uniqueItems: true, items: {
|
userIds: { type: 'array', uniqueItems: true, items: {
|
||||||
type: 'string', format: 'misskey:id',
|
type: 'string', format: 'misskey:id',
|
||||||
} },
|
} },
|
||||||
username: { type: 'string' },
|
|
||||||
host: { type: 'string', nullable: true },
|
|
||||||
},
|
},
|
||||||
required: [],
|
required: ['userIds'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
username: { type: 'string' },
|
||||||
|
host: {
|
||||||
|
type: 'string',
|
||||||
|
nullable: true,
|
||||||
|
description: 'The local host is represented with `null`.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ['username'],
|
||||||
|
},
|
||||||
|
],
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
// eslint-disable-next-line import/no-default-export
|
||||||
|
@ -24,17 +24,17 @@ export default async (ctx: Koa.Context) => {
|
|||||||
ctx.body = { error };
|
ctx.body = { error };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof username != 'string') {
|
if (typeof username !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof password != 'string') {
|
if (typeof password !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token != null && typeof token != 'string') {
|
if (token != null && typeof token !== 'string') {
|
||||||
ctx.status = 400;
|
ctx.status = 400;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|