diff --git a/.config/docker_example.yml b/.config/docker_example.yml index bd5eab492b..f8124bc9df 100644 --- a/.config/docker_example.yml +++ b/.config/docker_example.yml @@ -114,11 +114,6 @@ id: 'aid' # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Syslog option -#syslog: -# host: localhost -# port: 514 - # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/.config/example.yml b/.config/example.yml index cabf167fba..8fe41da15a 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -114,11 +114,6 @@ id: 'aid' # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Syslog option -#syslog: -# host: localhost -# port: 514 - # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/CHANGELOG.md b/CHANGELOG.md index 436c99a22c..36d9fcc4f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,15 @@ You should also include the user name that made the change. --> +## 13.3.0 (2023/02/03) +### Changes +- twitter/github/discord連携機能が削除されました +- ハッシュタグごとのチャートが削除されました +- syslogのサポートが削除されました + +### Improvements +- ロールで広告の非表示が有効になっている場合は最初から広告を非表示にするように + ## 13.2.6 (2023/02/01) ### Changes - docker-compose.ymlをdocker-compose.yml.exampleにしました。docker-compose.ymlとしてコピーしてから使用してください。 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4689543d50..811e4219e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ Thank you for your PR! Before creating a PR, please check the following: - Check if there are any documents that need to be created or updated due to this change. - If you have added a feature or fixed a bug, please add a test case if possible. - Please make sure that tests and Lint are passed in advance. - - You can run it with `yarn test` and `yarn lint`. [See more info](#testing) + - You can run it with `pnpm test` and `pnpm lint`. [See more info](#testing) - If this PR includes UI changes, please attach a screenshot in the text. Thanks for your cooperation 🤗 @@ -102,7 +102,7 @@ If your language is not listed in Crowdin, please open an issue. During development, it is useful to use the ``` -yarn dev +pnpm dev ``` command. @@ -112,7 +112,7 @@ command. - Service Worker is watched by esbuild. ## Testing -- Test codes are located in [`/test`](/test). +- Test codes are located in [`/packages/backend/test`](/packages/backend/test). ### Run test Create a config file. @@ -127,12 +127,12 @@ Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.y Run all test. ``` -yarn test +pnpm test ``` #### Run specify test ``` -yarn jest -- foo.ts +pnpm jest -- foo.ts ``` ### e2e tests @@ -177,9 +177,9 @@ vue-routerとの最大の違いは、niraxは複数のルーターが存在す これにより、アプリ内ウィンドウでブラウザとは個別にルーティングすることなどが可能になります。 ## Notes -### How to resolve conflictions occurred at yarn.lock? +### How to resolve conflictions occurred at pnpm-lock.yaml? -Just execute `yarn` to fix it. +Just execute `pnpm` to fix it. ### INSERTするときにはsaveではなくinsertを使用する #6441 @@ -265,7 +265,7 @@ MongoDBは`null`で返してきてたので、その感覚で`if (x === null)` ### Migration作成方法 packages/backendで: ```sh -yarn dlx typeorm migration:generate -d ormconfig.js -o +pnpm dlx typeorm migration:generate -d ormconfig.js -o ``` - 生成後、ファイルをmigration下に移してください diff --git a/README.md b/README.md index c273270644..c12882ca32 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ --- +[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey) +
diff --git a/chart/files/default.yml b/chart/files/default.yml index 862951d4d5..4061ca3eb6 100644 --- a/chart/files/default.yml +++ b/chart/files/default.yml @@ -133,11 +133,6 @@ id: "aid" # IP address family used for outgoing request (ipv4, ipv6 or dual) #outgoingAddressFamily: ipv4 -# Syslog option -#syslog: -# host: localhost -# port: 514 - # Proxy for HTTP/HTTPS #proxy: http://127.0.0.1:3128 diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 47799a0917..7323494d61 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -509,7 +509,7 @@ objectStorageSetPublicRead: "Seleccionar \"public-read\" al subir " serverLogs: "Registros del servidor" deleteAll: "Eliminar todos" showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo" -newNoteRecived: "Tienes una nota nuevo" +newNoteRecived: "Tienes una nota nueva" sounds: "Sonidos" sound: "Sonidos" listen: "Escuchar" @@ -918,14 +918,320 @@ tools: "Utilidades" cannotLoad: "No se puede cargar." numberOfProfileView: "Número de vistas de perfil" like: "¡Muy bien!" +unlike: "Quitar 'me gusta'" +numberOfLikes: "Cantidad de 'Me gusta'" show: "Apariencia" +neverShow: "No mostrar de nuevo" +remindMeLater: "Recordar después" +didYouLikeMisskey: "¿Te gusta Misskey?" +pleaseDonate: "Misskey es software libre, y es usado por {host} . Por favor, ¡considera donar al proyecto principal para que podamos continuar!" +roles: "Roles" +role: "Roles" +normalUser: "Usuario normal" +undefined: "Indefinido" +assign: "Asignar" +unassign: "Quitar" color: "Color" +manageCustomEmojis: "Administrar emojis personalizados" +youCannotCreateAnymore: "Se alcanzó el límite de creación" +cannotPerformTemporary: "Indisponible temporalmente" +cannotPerformTemporaryDescription: "Esta acción no se puede realizar porque se excedió el límite de ejecución. Espera un poco y prueba de nuevo." +preset: "Predefinido" +selectFromPresets: "Escoger desde predefinidos" +achievements: "Logros" +_achievements: + earnedAt: "Desbloqueado el" + _types: + _notes1: + title: "Configurando mis espacio" + description: "Publicar tu primera nota" + flavor: "¡Pasándola bien con Misskey!" + _notes10: + title: "Algunas notas" + description: "10 notas publicadas" + _notes100: + title: "¡Muchas notas!" + description: "100 notas publicadas" + _notes500: + title: "¡Cubierto de notas!" + description: "500 notas publicadas" + _notes1000: + title: "¡Una montaña de notas!" + description: "1000 notas publicadas" + _notes5000: + title: "¡Exceso de notas!" + description: "5000 notas publicadas" + _notes10000: + title: "¡Súpernota!" + description: "10000 notas publicadas" + _notes20000: + title: "Necesito... Más... ¡Notas!" + description: "20000 notas publicadas" + _notes30000: + title: "¡Notas! ¡Notas! ¡Notas!" + description: "30000 notas publicadas" + _notes40000: + title: "Fábrica de notas" + description: "40000 notas publicadas" + _notes50000: + title: "¡Un planeta de notas!" + description: "50000 notas publicadas" + _notes60000: + title: "¡Un cuásar de notas!" + description: "60000 notas publicadas" + _notes70000: + title: "¡Un hoyo negro de notas!" + description: "70000 notas publicadas" + _notes80000: + title: "¡Una galaxia de notas!" + description: "80000 notas publicadas" + _notes90000: + title: "¡Todo un universo de notas!" + description: "90000 notas publicadas" + _notes100000: + title: "ALL YOUR NOTE ARE BELONG TO US" + description: "100000 notas publicadas" + flavor: "¿Tienes tanto para publicar?" + _login3: + title: "Principiante I" + description: "Días desde el inicio de sesión: 3" + flavor: "Desde hoy, soy Misskero" + _login7: + title: "Principiante II" + description: "Días desde el inicio de sesión: 7" + flavor: "¿Ya te acostumbraste?" + _login15: + title: "Principiante III" + description: "Días desde el inicio de sesión: 15" + _login30: + title: "Misskero I" + description: "Días desde el inicio de sesión: 30" + _login60: + title: "Misskero II" + description: "Días desde el inicio de sesión: 60" + _login100: + title: "Misskero III" + description: "Días desde el inicio de sesión: 100" + flavor: "Para este usuario, Misskaína" + _login200: + title: "Regular I" + description: "Días desde el inicio de sesión: 200" + _login300: + title: "Regular II" + description: "Días desde el inicio de sesión: 300" + _login400: + title: "Regular III" + description: "Días desde el inicio de sesión: 400" + _login500: + title: "Veterano I" + description: "Días desde el inicio de sesión: 500" + flavor: "Chicos, me encantan las libretas..." + _login600: + title: "Veterano II" + description: "Días desde el inicio de sesión: 600" + _login700: + title: "Veterano III" + description: "Días desde el inicio de sesión: 700" + _login800: + title: "Maestro I" + description: "Días desde el inicio de sesión: 800" + _login900: + title: "Maestro II" + description: "Días desde el inicio de sesión: 900" + _login1000: + title: "Maestro III" + description: "Días desde el inicio de sesión: 1000" + flavor: "¡Gracias por usar Misskey!" + _noteClipped1: + title: "No puedo evitar clipearte..." + description: "Hacer un clip por primera vez" + _noteFavorited1: + title: "Contemplando las estrellas" + description: "Poner una nota como favorito por primera vez" + _myNoteFavorited1: + title: "¡Quiero una estrella!" + description: "Tu nota ha sido marcada como favorito por primera vez" + _profileFilled: + title: "¡Listo!" + description: "Perfil completado" + _markedAsCat: + title: "Soy un gato" + description: "Configurar la cuenta como cuenta de un gato" + flavor: "Aún no tengo nombre" + _following1: + title: "Primera vez siguiendo a alguien" + description: "Seguir a un usuario" + _following10: + title: "Ahí la llevas, ahí la llevas..." + description: "10 usuarios seguidos" + _following50: + title: "¡Un puñado de amigos!" + description: "50 cuentas seguidas" + _following100: + title: "100 amigos" + description: "100 cuentas seguidas" + _following300: + title: "¡Sobrecarga de amigos!" + description: "300 cuentas seguidas" + _followers1: + title: "¡Tu primer seguidor!" + description: "1 seguidor ganado" + _followers10: + title: "¡Sígueme!" + description: "10 seguidores ganados" + _followers50: + title: "Viniendo en manada" + description: "50 seguidores ganados" + _followers100: + title: "Popular" + description: "100 cuentas seguidas" + _followers300: + title: "Por favor, hagan una fila" + description: "300 seguidores ganados" + _followers500: + title: "¡Toda una torre de radio!" + description: "500 seguidores ganados" + _followers1000: + title: "\"Influyente\"" + description: "1000 seguidores gandos" + _collectAchievements30: + title: "Coleccionista" + description: "30 logros ganados" + _viewAchievements3min: + title: "¡Te gustan los logros!" + description: "Mirando tus logros por 3 minutos" + _iLoveMisskey: + title: "¡AMO Misskey!" + description: "\"I ❤ #Misskey\" Publicado" + flavor: "El equipo de desarrollo de Misskey, en verdad, ¡aprecia tu apoyo!" + _foundTreasure: + title: "Búsqueda del tesoro" + description: "Encontraste un tesoro" + _client30min: + title: "Un descansito" + description: "30 minutos dedicados a Misskey" + _noteDeletedWithin1min: + title: "Ah... Mejor no..." + description: "Borrar una nota antes que de pase 1 minuto" + _postedAtLateNight: + title: "Nocturno" + description: "Una nota publicada por la noche" + flavor: "¡Ya casi es hora de dormir!" + _postedAt0min0sec: + title: "Reloj parlante" + description: "Publicar una nota a las 00:00 de la madrugada" + flavor: "Tic, tic, tic ¡TUUUUUN!" + _selfQuote: + title: "Autoreferencia" + description: "Citar tu propia nota" + _htl20npm: + title: "Línea de tiempo fluyendo" + description: "La velocidad de tu línea de tiempo excede las 20 npm (notas por minuto)" + _viewInstanceChart: + title: "Analista" + description: "Gráficas de la instancia mostradas" + _outputHelloWorldOnScratchpad: + title: "¡Hola mundo!" + description: "Escribir \"hello world\" en el compositor" + _open3windows: + title: "Multiventana" + description: "Tener más de 3 ventanas al mismo tiempo" + _driveFolderCircularReference: + title: "Referencia circular" + description: "Intento de crear carpetas recursivamente" + _reactWithoutRead: + title: "¡Sí lo leíste bien?" + description: "Reaccionar a los 3 segundos de publicación de una nota con más de 100 caracteres" + _clickedClickHere: + title: "Pícale aquí" + description: "Le picó ahí" + _justPlainLucky: + title: "Pura suerte" + description: "Obtenido con una probabilidad del 0.01% cada 10 segundos" + _setNameToSyuilo: + title: "Complejo de superioridad" + description: "Configurar el nombre como 'Syuilo'" + _passedSinceAccountCreated1: + title: "Primer aniversario" + description: "Pasó un año desde la creación de la cuenta" + _passedSinceAccountCreated2: + title: "Segundo aniversario" + description: "Pasaron dos años desde la creación de la cuenta" + _passedSinceAccountCreated3: + title: "Tercer aniversario" + description: "Pasaron tres años desde la creación de la cuenta" + _loggedInOnBirthday: + title: "¡Feliz cumpleaños!" + description: "En linea el día de tu cumpleaños" + _loggedInOnNewYearsDay: + title: "¡Feliz Año Nuevo!" + description: "En linea en año nuevo" + flavor: "¡Gracias por tu apoyo a la instancia durante todo este año!" + _cookieClicked: + title: "Un juego para picarle a una galleta" + description: "Picaste una galleta" + flavor: "¿Está mal este juego?" + _brainDiver: + title: "Brain Diver" + description: "Publicaste un vínculo a \"Brain Diver\"" + flavor: "Misskey-Misskey La-Tu-Ma" _role: + new: "Crear rol" + edit: "Editar rol" + name: "Nombre del rol" + description: "Descripción del rol" + permission: "Permisos del rol" + descriptionOfPermission: "Moderador Te permite ejecutar acciones básicas de moderación.\nAdministradores puede cambiar todas las configuraciones de la instancia." + assignTarget: "Asignar objetivo" + descriptionOfAssignTarget: "Manual Para cambiar manualmente lo que se incluye en este rol.\nCondicional configura una condición, y los usuarios que cumplan la condición serán incluídos automáticamente." + manual: "manual" + conditional: "condicional" + condition: "condición" + isConditionalRole: "Esto es un rol condicional" + isPublic: "Publicar rol" + descriptionOfIsPublic: "Cualquiera puede ver los usuarios asignados a este rol. También, el perfil del usuario mostrará este rol." + options: "Opción" + policies: "Política" + baseRole: "Rol base" + useBaseValue: "Usar los valores del rol base" + chooseRoleToAssign: "Selecciona el rol para asignar" + canEditMembersByModerator: "Permitir a los moderadores editar los miembros" + descriptionOfCanEditMembersByModerator: "Si se activa, los moderadores, al igual que los administradores, serán capaces de asignar/quitar usuarios a éste rol. Si se desactiva, sólo los administradores podrán hacerlo." priority: "Prioridad" _priority: low: "Baja" middle: "Mediano" high: "Alta" + _options: + gtlAvailable: "Explorar la línea de tiempo global" + ltlAvailable: "Explorar la línea de tiempo local" + canPublicNote: "Permitir la publicación" + canInvite: "Puede crear códigos de invitación" + canManageCustomEmojis: "Administrar emojis personalizados" + driveCapacity: "Capacidad de almacenamiento" + pinMax: "Máximo de notas fijadas" + antennaMax: "Máximo de antenas" + wordMuteMax: "Máximo de caracteres en palabras silenciadas" + webhookMax: "Máximo de Webhooks" + clipMax: "Máximo de clips" + noteEachClipsMax: "Máximo de notas con clip" + userListMax: "Máximo de listas de usuarios" + userEachUserListsMax: "Máximo de usuarios en una lista" + rateLimitFactor: "Limitador" + descriptionOfRateLimitFactor: "Límites más bajos son menos restrictivos, más altos menos restrictivos" + canHideAds: "Puede ocultar anuncios" + _condition: + isLocal: "Usuario local" + isRemote: "Usuario remoto" + createdLessThan: "Menos de X han pasado desde la creación de la cuenta" + createdMoreThan: "Más de X han pasado desde la creación de la cuenta" + followersLessThanOrEq: "Tiene X o menos seguidores" + followersMoreThanOrEq: "Tiene X o más seguidores" + followingLessThanOrEq: "Sigue X o menos cuentas" + followingMoreThanOrEq: "Sigue X o más cuentas" + and: "Condicional AND" + or: "Condicional OR" + not: "Condicional NOT" _sensitiveMediaDetection: description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor." sensitivity: "Sensibilidad de detección" @@ -1328,10 +1634,12 @@ _widgets: jobQueue: "Cola de trabajos" serverMetric: "Estadísticas del servidor" aiscript: "Consola de AiScript" + aiscriptApp: "Aplicación AiScript" aichan: "indigo" userList: "Lista de usuarios" _userList: chooseList: "Seleccione una lista" + clicker: "Cliqueador" _cw: hide: "Ocultar" show: "Ver más" @@ -1434,7 +1742,16 @@ _timelines: social: "Social" global: "Global" _play: + new: "Crear guión" + edit: "Editar guión" + created: "Guión creado" + updated: "Guión editado" + deleted: "Guión eliminado" + pageSetting: "Configuración de guión" + editThisPage: "Editar este guión" viewSource: "Ver la fuente" + my: "Mis guiones" + liked: "Guiones que te gustaron" featured: "Popular" title: "Título" script: "Script" @@ -1507,6 +1824,7 @@ _notification: pollEnded: "Estan disponibles los resultados de la encuesta" unreadAntennaNote: "Antena {name}" emptyPushNotificationMessage: "Se han actualizado las notificaciones push" + achievementEarned: "Logro desbloqueado" _types: all: "Todo" follow: "Siguiendo" diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index e660635d93..52f3f41fcb 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -529,7 +529,7 @@ state: "Стан" sort: "Сортування" ascendingOrder: "За зростанням" descendingOrder: "За спаданням" -scratchpad: "Чернетка" +scratchpad: "Scratchpad" scratchpadDescription: "Scratchpad надає середовище для експериментів з AiScript. Ви можете писати, виконувати його і тестувати взаємодію з Misskey." output: "Вихід" script: "Скрипт" @@ -1084,22 +1084,32 @@ _achievements: description: "Перевищити швидкість домашньої стрічки 20npm (нотаток на хвилину)" _viewInstanceChart: title: "Аналітик" + _outputHelloWorldOnScratchpad: + title: "Hello, world!" + description: "Вивести \"hello world\" у Скретчпаді" _clickedClickHere: title: "Натисніть тут" description: "Натиснуто тут" + _justPlainLucky: + title: "Просто вдача" + description: "Можна отримати з ймовірністю 0,01% кожні 10 секунд" _setNameToSyuilo: title: "Комплекс бога" description: "Встановлено ім'я \"syuilo\"" _passedSinceAccountCreated1: title: "Перша річниця" + description: "Минув рік з моменту створення акаунта" _passedSinceAccountCreated2: title: "Друга річниця" + description: "Минуло 2 роки з моменту створення акаунта" _passedSinceAccountCreated3: title: "Третя річниця" description: "Минуло 3 роки з моменту створення акаунта" _loggedInOnBirthday: title: "З Днем народження!" + description: "Увійти у свій день народження" _loggedInOnNewYearsDay: + title: "З Новим роком!" description: "Увійшли в перший день року" _brainDiver: title: "Brain Diver" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 651221fe6c..fba04147d9 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -1083,7 +1083,7 @@ _achievements: title: "排列成行" description: "关注者超过300人" _followers500: - title: "风向标" + title: "信号塔" description: "关注者超过500人" _collectAchievements30: title: "成就收藏家" @@ -1104,7 +1104,7 @@ _achievements: title: "无话可说" description: "发帖后一分钟内就将其删除" _postedAtLateNight: - title: "夜行者" + title: "夜猫子" description: "深夜发布帖子" flavor: "差不多该去睡了喔。" _postedAt0min0sec: @@ -1114,6 +1114,12 @@ _achievements: _selfQuote: title: "自我提及" description: "引用了自己的帖子" + _htl20npm: + title: "流动的时间线" + description: "在首页时间线的流速超过20npm" + _viewInstanceChart: + title: "分析师" + description: "查看了实例信息中的图表" _outputHelloWorldOnScratchpad: title: "Hello, world!" _open3windows: @@ -1129,7 +1135,7 @@ _achievements: description: "点了这里" _justPlainLucky: title: "超高校级的幸运" - description: "每10秒有0.01的概率获得" + description: "每10秒有0.01的概率自动获得" _setNameToSyuilo: title: "像神一样呐" description: "将名称设定为syuilo" diff --git a/package.json b/package.json index 2cb239641a..09d34690e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.2.6", + "version": "13.3.0", "codename": "nasubi", "repository": { "type": "git", @@ -38,8 +38,8 @@ "cleanall": "pnpm clean-all" }, "resolutions": { - "chokidar": "^3.5.3", - "lodash": "^4.17.21" + "chokidar": "3.5.3", + "lodash": "4.17.21" }, "dependencies": { "execa": "5.1.1", @@ -49,19 +49,19 @@ "gulp-replace": "1.1.4", "gulp-terser": "2.1.0", "js-yaml": "4.1.0", - "typescript": "4.9.4" + "typescript": "4.9.5" }, "devDependencies": { "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", - "@typescript-eslint/eslint-plugin": "5.49.0", - "@typescript-eslint/parser": "5.49.0", + "@typescript-eslint/eslint-plugin": "5.50.0", + "@typescript-eslint/parser": "5.50.0", "cross-env": "7.0.3", - "cypress": "12.4.0", - "eslint": "^8.32.0", + "cypress": "12.5.1", + "eslint": "8.33.0", "start-server-and-test": "1.15.3" }, "optionalDependencies": { - "@tensorflow/tfjs-core": "^4.2.0" + "@tensorflow/tfjs-core": "4.2.0" } } diff --git a/packages/backend/migration/1675404035646-cleanup.js b/packages/backend/migration/1675404035646-cleanup.js new file mode 100644 index 0000000000..09b22ee393 --- /dev/null +++ b/packages/backend/migration/1675404035646-cleanup.js @@ -0,0 +1,29 @@ +export class cleanup1675404035646 { + name = 'cleanup1675404035646' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTwitterIntegration"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGithubIntegration"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableDiscordIntegration"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerKey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "twitterConsumerSecret"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientId"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "githubClientSecret"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientId"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "discordClientSecret"`); + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "integrations"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "integrations" jsonb NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientSecret" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "discordClientId" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientSecret" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "githubClientId" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerSecret" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "twitterConsumerKey" character varying(128)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "enableDiscordIntegration" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "enableGithubIntegration" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "enableTwitterIntegration" boolean NOT NULL DEFAULT false`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index f68fde8b4c..762cb4b4d5 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -19,27 +19,27 @@ "test-and-coverage": "pnpm jest-and-coverage" }, "optionalDependencies": { - "@tensorflow/tfjs": "^4.2.0", + "@tensorflow/tfjs": "4.2.0", "@tensorflow/tfjs-node": "4.2.0" }, "dependencies": { - "@bull-board/api": "^4.11.0", - "@bull-board/fastify": "^4.11.0", - "@bull-board/ui": "^4.11.0", + "@bull-board/api": "4.11.0", + "@bull-board/fastify": "4.11.0", + "@bull-board/ui": "4.11.0", "@discordapp/twemoji": "14.0.2", "@fastify/accepts": "4.1.0", - "@fastify/cookie": "^8.3.0", + "@fastify/cookie": "8.3.0", "@fastify/cors": "8.2.0", - "@fastify/http-proxy": "^8.4.0", + "@fastify/http-proxy": "8.4.0", "@fastify/multipart": "7.4.0", - "@fastify/static": "6.7.0", + "@fastify/static": "6.8.0", "@fastify/view": "7.4.1", - "@nestjs/common": "9.2.1", - "@nestjs/core": "9.2.1", - "@nestjs/testing": "9.2.1", + "@nestjs/common": "9.3.1", + "@nestjs/core": "9.3.1", + "@nestjs/testing": "9.3.1", "@peertube/http-signature": "1.7.0", "@sinonjs/fake-timers": "10.0.2", - "accepts": "^1.3.8", + "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "5.3.1", "autwh": "0.1.0", @@ -62,11 +62,11 @@ "feed": "4.2.2", "file-type": "18.2.0", "fluent-ffmpeg": "2.1.2", - "form-data": "^4.0.0", - "got": "^12.5.3", + "form-data": "4.0.0", + "got": "12.5.3", "hpagent": "1.2.0", "ioredis": "4.28.5", - "ip-cidr": "3.0.11", + "ip-cidr": "3.1.0", "is-svg": "4.3.2", "js-yaml": "4.1.0", "jsdom": "21.1.0", @@ -75,15 +75,16 @@ "jsrsasign": "10.6.1", "mfm-js": "0.23.3", "mime-types": "2.1.35", - "misskey-js": "0.0.14", + "misskey-js": "0.0.15", "ms": "3.0.0-canary.1", "nested-property": "4.0.0", - "nodemailer": "6.9.0", + "node-fetch": "3.3.0", + "nodemailer": "6.9.1", "nsfwjs": "2.4.2", - "oauth": "^0.10.0", + "oauth": "0.10.0", "os-utils": "0.0.14", "parse5": "7.1.2", - "pg": "8.8.0", + "pg": "8.9.0", "private-ip": "3.0.0", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", @@ -101,23 +102,22 @@ "rss-parser": "3.12.0", "rxjs": "7.8.0", "s-age": "1.1.2", - "sanitize-html": "2.8.1", - "seedrandom": "^3.0.5", + "sanitize-html": "2.9.0", + "seedrandom": "3.0.5", "semver": "7.3.8", "sharp": "0.31.3", "speakeasy": "2.0.0", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", "summaly": "2.7.0", - "syslog-pro": "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2", - "systeminformation": "5.17.4", + "systeminformation": "5.17.8", "tinycolor2": "1.5.2", "tmp": "0.2.1", "tsc-alias": "1.8.2", "tsconfig-paths": "4.1.2", "twemoji-parser": "14.0.0", "typeorm": "0.3.11", - "typescript": "4.9.4", + "typescript": "4.9.5", "ulid": "2.3.0", "unzipper": "0.10.11", "uuid": "9.0.0", @@ -125,21 +125,21 @@ "web-push": "3.5.0", "websocket": "1.0.34", "ws": "8.12.0", - "xev": "3.0.2", - "node-fetch": "3.3.0" + "xev": "3.0.2" }, "devDependencies": { - "@redocly/openapi-core": "1.0.0-beta.120", - "@swc/cli": "^0.1.59", - "@swc/core": "1.3.29", + "@jest/globals": "29.4.1", + "@redocly/openapi-core": "1.0.0-beta.123", + "@swc/cli": "0.1.61", + "@swc/core": "1.3.32", "@swc/jest": "0.2.24", "@types/accepts": "1.3.5", "@types/archiver": "5.3.1", "@types/bcryptjs": "2.4.2", "@types/bull": "4.10.0", "@types/cbor": "6.0.0", - "@types/color-convert": "^2.0.0", - "@types/content-disposition": "^0.5.5", + "@types/color-convert": "2.0.0", + "@types/content-disposition": "0.5.5", "@types/escape-regexp": "0.0.1", "@types/fluent-ffmpeg": "2.1.20", "@types/ioredis": "4.28.10", @@ -166,7 +166,6 @@ "@types/sharp": "0.31.1", "@types/sinonjs__fake-timers": "8.1.2", "@types/speakeasy": "2.0.7", - "@types/syslog-pro": "^1.0.0", "@types/tinycolor2": "1.4.3", "@types/tmp": "0.2.3", "@types/unzipper": "0.10.5", @@ -175,13 +174,13 @@ "@types/web-push": "3.3.2", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.49.0", - "@typescript-eslint/parser": "5.49.0", + "@typescript-eslint/eslint-plugin": "5.50.0", + "@typescript-eslint/parser": "5.50.0", "cross-env": "7.0.3", - "eslint": "8.32.0", + "eslint": "8.33.0", "eslint-plugin-import": "2.27.5", "execa": "6.1.0", "jest": "29.4.1", - "jest-mock": "^29.4.1" + "jest-mock": "29.4.1" } } diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index 025d7acdeb..1d4e700656 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -65,11 +65,6 @@ export type Source = { deliverJobMaxAttempts?: number; inboxJobMaxAttempts?: number; - syslog: { - host: string; - port: number; - }; - mediaProxy?: string; proxyRemoteFiles?: boolean; @@ -113,7 +108,7 @@ const path = process.env.NODE_ENV === 'test' export function loadConfig() { const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8')); - const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json') + const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json'); const clientManifest = clientManifestExists ? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8')) : { 'src/init.ts': { file: 'src/init.ts' } }; diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index eddf407940..6a6d1b864a 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -62,7 +62,6 @@ import PerUserNotesChart from './chart/charts/per-user-notes.js'; import PerUserPvChart from './chart/charts/per-user-pv.js'; import DriveChart from './chart/charts/drive.js'; import PerUserReactionsChart from './chart/charts/per-user-reactions.js'; -import HashtagChart from './chart/charts/hashtag.js'; import PerUserFollowingChart from './chart/charts/per-user-following.js'; import PerUserDriveChart from './chart/charts/per-user-drive.js'; import ApRequestChart from './chart/charts/ap-request.js'; @@ -187,7 +186,6 @@ const $PerUserNotesChart: Provider = { provide: 'PerUserNotesChart', useExisting const $PerUserPvChart: Provider = { provide: 'PerUserPvChart', useExisting: PerUserPvChart }; const $DriveChart: Provider = { provide: 'DriveChart', useExisting: DriveChart }; const $PerUserReactionsChart: Provider = { provide: 'PerUserReactionsChart', useExisting: PerUserReactionsChart }; -const $HashtagChart: Provider = { provide: 'HashtagChart', useExisting: HashtagChart }; const $PerUserFollowingChart: Provider = { provide: 'PerUserFollowingChart', useExisting: PerUserFollowingChart }; const $PerUserDriveChart: Provider = { provide: 'PerUserDriveChart', useExisting: PerUserDriveChart }; const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRequestChart }; @@ -315,7 +313,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserPvChart, DriveChart, PerUserReactionsChart, - HashtagChart, PerUserFollowingChart, PerUserDriveChart, ApRequestChart, @@ -437,7 +434,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserPvChart, $DriveChart, $PerUserReactionsChart, - $HashtagChart, $PerUserFollowingChart, $PerUserDriveChart, $ApRequestChart, @@ -559,7 +555,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserPvChart, DriveChart, PerUserReactionsChart, - HashtagChart, PerUserFollowingChart, PerUserDriveChart, ApRequestChart, @@ -680,7 +675,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserPvChart, $DriveChart, $PerUserReactionsChart, - $HashtagChart, $PerUserFollowingChart, $PerUserDriveChart, $ApRequestChart, diff --git a/packages/backend/src/core/HashtagService.ts b/packages/backend/src/core/HashtagService.ts index 309cfe8c3f..851e42e7ba 100644 --- a/packages/backend/src/core/HashtagService.ts +++ b/packages/backend/src/core/HashtagService.ts @@ -4,7 +4,6 @@ import type { User } from '@/models/entities/User.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { IdService } from '@/core/IdService.js'; import type { Hashtag } from '@/models/entities/Hashtag.js'; -import HashtagChart from '@/core/chart/charts/hashtag.js'; import type { HashtagsRepository, UsersRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; @@ -20,7 +19,6 @@ export class HashtagService { private userEntityService: UserEntityService, private idService: IdService, - private hashtagChart: HashtagChart, ) { } @@ -143,9 +141,5 @@ export class HashtagService { } as Hashtag); } } - - if (!isUserAttached) { - this.hashtagChart.update(tag, user); - } } } diff --git a/packages/backend/src/core/HttpRequestService.ts b/packages/backend/src/core/HttpRequestService.ts index baf74acfa6..e32026b04f 100644 --- a/packages/backend/src/core/HttpRequestService.ts +++ b/packages/backend/src/core/HttpRequestService.ts @@ -95,7 +95,7 @@ export class HttpRequestService { } @bindThis - public async getJson(url: string, accept = 'application/json, */*', headers?: Record): Promise { + public async getJson(url: string, accept = 'application/json, */*', headers?: Record): Promise { const res = await this.send(url, { method: 'GET', headers: Object.assign({ @@ -106,7 +106,7 @@ export class HttpRequestService { size: 1024 * 256, }); - return await res.json(); + return await res.json() as T; } @bindThis diff --git a/packages/backend/src/core/LoggerService.ts b/packages/backend/src/core/LoggerService.ts index 221631f129..441c254f48 100644 --- a/packages/backend/src/core/LoggerService.ts +++ b/packages/backend/src/core/LoggerService.ts @@ -1,5 +1,4 @@ import { Inject, Injectable } from '@nestjs/common'; -import * as SyslogPro from 'syslog-pro'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import Logger from '@/logger.js'; @@ -8,29 +7,14 @@ import type { KEYWORD } from 'color-convert/conversions'; @Injectable() export class LoggerService { - private syslogClient; - constructor( @Inject(DI.config) private config: Config, ) { - if (this.config.syslog) { - this.syslogClient = new SyslogPro.RFC5424({ - applicationName: 'Misskey', - timestamp: true, - includeStructuredData: true, - color: true, - extendedColor: true, - server: { - target: config.syslog.host, - port: config.syslog.port, - }, - }); - } } @bindThis public getLogger(domain: string, color?: KEYWORD | undefined, store?: boolean) { - return new Logger(domain, color, store, this.syslogClient); + return new Logger(domain, color, store); } } diff --git a/packages/backend/src/core/NoteReadService.ts b/packages/backend/src/core/NoteReadService.ts index 82825b8b15..f4395725d6 100644 --- a/packages/backend/src/core/NoteReadService.ts +++ b/packages/backend/src/core/NoteReadService.ts @@ -9,9 +9,9 @@ import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { UsersRepository, NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository, FollowingsRepository, ChannelFollowingsRepository, AntennaNotesRepository } from '@/models/index.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import { NotificationService } from './NotificationService.js'; import { AntennaService } from './AntennaService.js'; -import { bindThis } from '@/decorators.js'; import { PushNotificationService } from './PushNotificationService.js'; @Injectable() @@ -107,12 +107,6 @@ export class NoteReadService { followingChannels: Set; }, ): Promise { - const following = info?.following ? info.following : new Set((await this.followingsRepository.find({ - where: { - followerId: userId, - }, - select: ['followeeId'], - })).map(x => x.followeeId)); const followingChannels = info?.followingChannels ? info.followingChannels : new Set((await this.channelFollowingsRepository.find({ where: { followerId: userId, @@ -139,7 +133,7 @@ export class NoteReadService { if (note.user != null) { // たぶんnullになることは無いはずだけど一応 for (const antenna of myAntennas) { - if (await this.antennaService.checkHitAntenna(antenna, note, note.user, undefined, Array.from(following))) { + if (await this.antennaService.checkHitAntenna(antenna, note, note.user)) { readAntennaNotes.push(note); } } diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 36110490a0..30caa9682c 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -44,16 +44,25 @@ export class WebhookService implements OnApplicationShutdown { switch (type) { case 'webhookCreated': if (body.active) { - this.webhooks.push(body); + this.webhooks.push({ + ...body, + createdAt: new Date(body.createdAt), + }); } break; case 'webhookUpdated': if (body.active) { const i = this.webhooks.findIndex(a => a.id === body.id); if (i > -1) { - this.webhooks[i] = body; + this.webhooks[i] = { + ...body, + createdAt: new Date(body.createdAt), + }; } else { - this.webhooks.push(body); + this.webhooks.push({ + ...body, + createdAt: new Date(body.createdAt), + }); } } else { this.webhooks = this.webhooks.filter(a => a.id !== body.id); diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 91a2767e69..648f30229a 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -274,7 +274,7 @@ export class ApRendererService { } as any; if (reaction.startsWith(':')) { - const name = reaction.replace(/:/g, ''); + const name = reaction.replaceAll(':', ''); const emoji = await this.emojisRepository.findOneBy({ name, host: IsNull(), diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index f86b5e6f96..2325bbe093 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -29,6 +29,7 @@ import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { StatusError } from '@/misc/status-error.js'; import type { UtilityService } from '@/core/UtilityService.js'; import type { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { bindThis } from '@/decorators.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -43,37 +44,6 @@ import type { IActor, IObject, IApPropertyValue } from '../type.js'; const nameLength = 128; const summaryLength = 2048; -const services: { - [x: string]: (id: string, username: string) => any -} = { - 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }), - 'misskey:authentication:github': (id, login) => ({ id, login }), - 'misskey:authentication:discord': (id, name) => $discord(id, name), -}; - -const $discord = (id: string, name: string) => { - if (typeof name !== 'string') { - name = 'unknown#0000'; - } - const [username, discriminator] = name.split('#'); - return { id, username, discriminator }; -}; - -function addService(target: { [x: string]: any }, source: IApPropertyValue) { - const service = services[source.name]; - - if (typeof source.value !== 'string') { - source.value = 'unknown'; - } - - const [id, username] = source.value.split('@'); - - if (service) { - target[source.name.split(':')[2]] = service(id, username); - } -} -import { bindThis } from '@/decorators.js'; - @Injectable() export class ApPersonService implements OnModuleInit { private utilityService: UtilityService; @@ -540,22 +510,16 @@ export class ApPersonService implements OnModuleInit { name: string, value: string }[] = []; - const services: { [x: string]: any } = {}; - if (Array.isArray(attachments)) { for (const attachment of attachments.filter(isPropertyValue)) { - if (isPropertyValue(attachment.identifier)) { - addService(services, attachment.identifier); - } else { - fields.push({ - name: attachment.name, - value: this.mfmService.fromHtml(attachment.value), - }); - } + fields.push({ + name: attachment.name, + value: this.mfmService.fromHtml(attachment.value), + }); } } - return { fields, services }; + return { fields }; } @bindThis diff --git a/packages/backend/src/core/chart/ChartManagementService.ts b/packages/backend/src/core/chart/ChartManagementService.ts index 4fba1b57d0..779a32ac5e 100644 --- a/packages/backend/src/core/chart/ChartManagementService.ts +++ b/packages/backend/src/core/chart/ChartManagementService.ts @@ -10,7 +10,6 @@ import PerUserNotesChart from './charts/per-user-notes.js'; import PerUserPvChart from './charts/per-user-pv.js'; import DriveChart from './charts/drive.js'; import PerUserReactionsChart from './charts/per-user-reactions.js'; -import HashtagChart from './charts/hashtag.js'; import PerUserFollowingChart from './charts/per-user-following.js'; import PerUserDriveChart from './charts/per-user-drive.js'; import ApRequestChart from './charts/ap-request.js'; @@ -31,7 +30,6 @@ export class ChartManagementService implements OnApplicationShutdown { private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, - private hashtagChart: HashtagChart, private perUserFollowingChart: PerUserFollowingChart, private perUserDriveChart: PerUserDriveChart, private apRequestChart: ApRequestChart, @@ -46,7 +44,6 @@ export class ChartManagementService implements OnApplicationShutdown { this.perUserPvChart, this.driveChart, this.perUserReactionsChart, - this.hashtagChart, this.perUserFollowingChart, this.perUserDriveChart, this.apRequestChart, diff --git a/packages/backend/src/core/chart/charts/entities/hashtag.ts b/packages/backend/src/core/chart/charts/entities/hashtag.ts deleted file mode 100644 index 4d04039047..0000000000 --- a/packages/backend/src/core/chart/charts/entities/hashtag.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Chart from '../../core.js'; - -export const name = 'hashtag'; - -export const schema = { - 'local.users': { uniqueIncrement: true }, - 'remote.users': { uniqueIncrement: true }, -} as const; - -export const entity = Chart.schemaToEntity(name, schema, true); diff --git a/packages/backend/src/core/chart/charts/hashtag.ts b/packages/backend/src/core/chart/charts/hashtag.ts deleted file mode 100644 index 3899b41363..0000000000 --- a/packages/backend/src/core/chart/charts/hashtag.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Injectable, Inject } from '@nestjs/common'; -import { DataSource } from 'typeorm'; -import type { User } from '@/models/entities/User.js'; -import { AppLockService } from '@/core/AppLockService.js'; -import { DI } from '@/di-symbols.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { bindThis } from '@/decorators.js'; -import Chart from '../core.js'; -import { ChartLoggerService } from '../ChartLoggerService.js'; -import { name, schema } from './entities/hashtag.js'; -import type { KVs } from '../core.js'; - -/** - * ハッシュタグに関するチャート - */ -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class HashtagChart extends Chart { - constructor( - @Inject(DI.db) - private db: DataSource, - - private appLockService: AppLockService, - private userEntityService: UserEntityService, - private chartLoggerService: ChartLoggerService, - ) { - super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true); - } - - protected async tickMajor(): Promise>> { - return {}; - } - - protected async tickMinor(): Promise>> { - return {}; - } - - @bindThis - public async update(hashtag: string, user: { id: User['id'], host: User['host'] }): Promise { - await this.commit({ - 'local.users': this.userEntityService.isLocalUser(user) ? [user.id] : [], - 'remote.users': this.userEntityService.isLocalUser(user) ? [] : [user.id], - }, hashtag); - } -} diff --git a/packages/backend/src/core/chart/core.ts b/packages/backend/src/core/chart/core.ts index 2092b13b7e..d352adcc1f 100644 --- a/packages/backend/src/core/chart/core.ts +++ b/packages/backend/src/core/chart/core.ts @@ -11,9 +11,9 @@ import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import type { Repository, DataSource } from 'typeorm'; -const columnPrefix = '___' as const; -const uniqueTempColumnPrefix = 'unique_temp___' as const; -const columnDot = '_' as const; +const COLUMN_PREFIX = '___' as const; +const UNIQUE_TEMP_COLUMN_PREFIX = 'unique_temp___' as const; +const COLUMN_DELIMITER = '_' as const; type Schema = Record; -type KeyToColumnName = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof columnDot}${KeyToColumnName}` : T; +type KeyToColumnName = T extends `${infer R1}.${infer R2}` ? `${R1}${typeof COLUMN_DELIMITER}${KeyToColumnName}` : T; type Columns = { - [K in keyof S as `${typeof columnPrefix}${KeyToColumnName}`]: number; + [K in keyof S as `${typeof COLUMN_PREFIX}${KeyToColumnName}`]: number; }; type TempColumnsForUnique = { - [K in keyof S as `${typeof uniqueTempColumnPrefix}${KeyToColumnName}`]: S[K]['uniqueIncrement'] extends true ? string[] : never; + [K in keyof S as `${typeof UNIQUE_TEMP_COLUMN_PREFIX}${KeyToColumnName}`]: S[K]['uniqueIncrement'] extends true ? string[] : never; }; type RawRecord = { @@ -138,20 +138,20 @@ export default abstract class Chart { private static convertSchemaToColumnDefinitions(schema: Schema): Record { const columns = {} as Record; for (const [k, v] of Object.entries(schema)) { - const name = k.replaceAll('.', columnDot); + const name = k.replaceAll('.', COLUMN_DELIMITER); const type = v.range === 'big' ? 'bigint' : v.range === 'small' ? 'smallint' : 'integer'; if (v.uniqueIncrement) { - columns[uniqueTempColumnPrefix + name] = { + columns[UNIQUE_TEMP_COLUMN_PREFIX + name] = { type: 'varchar', array: true, default: '{}', }; - columns[columnPrefix + name] = { + columns[COLUMN_PREFIX + name] = { type, default: 0, }; } else { - columns[columnPrefix + name] = { + columns[COLUMN_PREFIX + name] = { type, default: 0, }; @@ -253,8 +253,8 @@ export default abstract class Chart { @bindThis private convertRawRecord(x: RawRecord): KVs { const kvs = {} as Record; - for (const k of Object.keys(x).filter((k) => k.startsWith(columnPrefix)) as (keyof Columns)[]) { - kvs[(k as string).substr(columnPrefix.length).split(columnDot).join('.')] = x[k] as unknown as number; + for (const k of Object.keys(x).filter((k) => k.startsWith(COLUMN_PREFIX)) as (keyof Columns)[]) { + kvs[(k as string).substr(COLUMN_PREFIX.length).split(COLUMN_DELIMITER).join('.')] = x[k] as unknown as number; } return kvs as KVs; } @@ -357,8 +357,8 @@ export default abstract class Chart { const columns = {} as Record; for (const [k, v] of Object.entries(data)) { - const name = k.replaceAll('.', columnDot); - columns[columnPrefix + name] = v; + const name = k.replaceAll('.', COLUMN_DELIMITER); + columns[COLUMN_PREFIX + name] = v; } // 新規ログ挿入 @@ -419,13 +419,13 @@ export default abstract class Chart { const queryForDay: Record, number | (() => string)> = {} as any; for (const [k, v] of Object.entries(finalDiffs)) { if (typeof v === 'number') { - const name = columnPrefix + k.replaceAll('.', columnDot) as string & keyof Columns; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as string & keyof Columns; if (v > 0) queryForHour[name] = () => `"${name}" + ${v}`; if (v < 0) queryForHour[name] = () => `"${name}" - ${Math.abs(v)}`; if (v > 0) queryForDay[name] = () => `"${name}" + ${v}`; if (v < 0) queryForDay[name] = () => `"${name}" - ${Math.abs(v)}`; } else if (Array.isArray(v) && v.length > 0) { // ユニークインクリメント - const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as string & keyof TempColumnsForUnique; + const tempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as string & keyof TempColumnsForUnique; // TODO: item をSQLエスケープ const itemsForHour = v.filter(item => !(logHour[tempColumnName] as unknown as string[]).includes(item)).map(item => `"${item}"`); const itemsForDay = v.filter(item => !(logDay[tempColumnName] as unknown as string[]).includes(item)).map(item => `"${item}"`); @@ -437,8 +437,8 @@ export default abstract class Chart { // bake unique count for (const [k, v] of Object.entries(finalDiffs)) { if (this.schema[k].uniqueIncrement) { - const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; - const tempColumnName = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof Columns; + const tempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; queryForHour[name] = new Set([...(v as string[]), ...(logHour[tempColumnName] as unknown as string[])]).size; queryForDay[name] = new Set([...(v as string[]), ...(logDay[tempColumnName] as unknown as string[])]).size; } @@ -449,15 +449,15 @@ export default abstract class Chart { for (const [k, v] of Object.entries(this.schema)) { const intersection = v.intersection; if (intersection) { - const name = columnPrefix + k.replaceAll('.', columnDot) as keyof Columns; + const name = COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof Columns; const firstKey = intersection[0]; - const firstTempColumnName = uniqueTempColumnPrefix + firstKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const firstTempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + firstKey.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; const firstValues = finalDiffs[firstKey] as string[] | undefined; const currentValuesForHour = new Set([...(firstValues ?? []), ...(logHour[firstTempColumnName] as unknown as string[])]); const currentValuesForDay = new Set([...(firstValues ?? []), ...(logDay[firstTempColumnName] as unknown as string[])]); for (let i = 1; i < intersection.length; i++) { const targetKey = intersection[i]; - const targetTempColumnName = uniqueTempColumnPrefix + targetKey.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const targetTempColumnName = UNIQUE_TEMP_COLUMN_PREFIX + targetKey.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; const targetValues = finalDiffs[targetKey] as string[] | undefined; const targetValuesForHour = new Set([...(targetValues ?? []), ...(logHour[targetTempColumnName] as unknown as string[])]); const targetValuesForDay = new Set([...(targetValues ?? []), ...(logDay[targetTempColumnName] as unknown as string[])]); @@ -510,7 +510,7 @@ export default abstract class Chart { const columns = {} as Record, number>; for (const [k, v] of Object.entries(data) as ([keyof typeof data, number])[]) { - const name = columnPrefix + (k as string).replaceAll('.', columnDot) as keyof Columns; + const name = COLUMN_PREFIX + (k as string).replaceAll('.', COLUMN_DELIMITER) as keyof Columns; columns[name] = v; } @@ -556,7 +556,7 @@ export default abstract class Chart { const columns = {} as Record, []>; for (const [k, v] of Object.entries(this.schema)) { if (v.uniqueIncrement) { - const name = uniqueTempColumnPrefix + k.replaceAll('.', columnDot) as keyof TempColumnsForUnique; + const name = UNIQUE_TEMP_COLUMN_PREFIX + k.replaceAll('.', COLUMN_DELIMITER) as keyof TempColumnsForUnique; columns[name] = []; } } diff --git a/packages/backend/src/core/chart/entities.ts b/packages/backend/src/core/chart/entities.ts index c2759e8b3c..b44e2e38b7 100644 --- a/packages/backend/src/core/chart/entities.ts +++ b/packages/backend/src/core/chart/entities.ts @@ -7,7 +7,6 @@ import { entity as PerUserNotesChart } from './charts/entities/per-user-notes.js import { entity as PerUserPvChart } from './charts/entities/per-user-pv.js'; import { entity as DriveChart } from './charts/entities/drive.js'; import { entity as PerUserReactionsChart } from './charts/entities/per-user-reactions.js'; -import { entity as HashtagChart } from './charts/entities/hashtag.js'; import { entity as PerUserFollowingChart } from './charts/entities/per-user-following.js'; import { entity as PerUserDriveChart } from './charts/entities/per-user-drive.js'; import { entity as ApRequestChart } from './charts/entities/ap-request.js'; @@ -27,7 +26,6 @@ export const entities = [ PerUserPvChart.hour, PerUserPvChart.day, DriveChart.hour, DriveChart.day, PerUserReactionsChart.hour, PerUserReactionsChart.day, - HashtagChart.hour, HashtagChart.day, PerUserFollowingChart.hour, PerUserFollowingChart.day, PerUserDriveChart.hour, PerUserDriveChart.day, ApRequestChart.hour, ApRequestChart.day, diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index ded1b512a1..4140b3f35e 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -2,7 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; import { ModuleRef } from '@nestjs/core'; import { DI } from '@/di-symbols.js'; -import type { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository } from '@/models/index.js'; +import type { AccessTokensRepository, NoteReactionsRepository, NotificationsRepository, User } from '@/models/index.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { Notification } from '@/models/entities/Notification.js'; import type { NoteReaction } from '@/models/entities/NoteReaction.js'; diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 546e61a26e..aaa80033b3 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -489,7 +489,6 @@ export class UserEntityService implements OnModuleInit { hasUnreadMessagingMessage: this.getHasUnreadMessagingMessage(user.id), hasUnreadNotification: this.getHasUnreadNotification(user.id), hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), - integrations: profile!.integrations, mutedWords: profile!.mutedWords, mutedInstances: profile!.mutedInstances, mutingNotificationTypes: profile!.mutingNotificationTypes, diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts index 5d275bc7b2..87f9b12313 100644 --- a/packages/backend/src/logger.ts +++ b/packages/backend/src/logger.ts @@ -17,15 +17,13 @@ export default class Logger { private context: Context; private parentLogger: Logger | null = null; private store: boolean; - private syslogClient: any | null = null; - constructor(context: string, color?: KEYWORD, store = true, syslogClient = null) { + constructor(context: string, color?: KEYWORD, store = true) { this.context = { name: context, color: color, }; this.store = store; - this.syslogClient = syslogClient; } @bindThis @@ -69,20 +67,6 @@ export default class Logger { console.log(important ? chalk.bold(log) : log); if (level === 'error' && data) console.log(data); - - if (store) { - if (this.syslogClient) { - const send = - level === 'error' ? this.syslogClient.error : - level === 'warning' ? this.syslogClient.warning : - level === 'success' ? this.syslogClient.info : - level === 'debug' ? this.syslogClient.info : - level === 'info' ? this.syslogClient.info : - null as never; - - send.bind(this.syslogClient)(message).catch(() => {}); - } - } } @bindThis diff --git a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts index 8fb3f4b19e..14c25922ad 100644 --- a/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts +++ b/packages/backend/src/misc/extract-custom-emojis-from-mfm.ts @@ -4,7 +4,7 @@ import { unique } from '@/misc/prelude/array.js'; export function extractCustomEmojisFromMfm(nodes: mfm.MfmNode[]): string[] { const emojiNodes = mfm.extract(nodes, (node) => { return (node.type === 'emojiCode' && node.props.name.length <= 100); - }); + }) as mfm.MfmEmojiCode[]; return unique(emojiNodes.map(x => x.props.name)); } diff --git a/packages/backend/src/misc/extract-hashtags.ts b/packages/backend/src/misc/extract-hashtags.ts index f8cabda3d6..d293fd7f52 100644 --- a/packages/backend/src/misc/extract-hashtags.ts +++ b/packages/backend/src/misc/extract-hashtags.ts @@ -2,7 +2,7 @@ import * as mfm from 'mfm-js'; import { unique } from '@/misc/prelude/array.js'; export function extractHashtags(nodes: mfm.MfmNode[]): string[] { - const hashtagNodes = mfm.extract(nodes, (node) => node.type === 'hashtag'); + const hashtagNodes = mfm.extract(nodes, (node) => node.type === 'hashtag') as mfm.MfmHashtag[]; const hashtags = unique(hashtagNodes.map(x => x.props.hashtag)); return hashtags; diff --git a/packages/backend/src/misc/nyaize.ts b/packages/backend/src/misc/nyaize.ts index 500d1db2cb..350f8d2172 100644 --- a/packages/backend/src/misc/nyaize.ts +++ b/packages/backend/src/misc/nyaize.ts @@ -1,14 +1,14 @@ export function nyaize(text: string): string { return text // ja-JP - .replace(/な/g, 'にゃ').replace(/ナ/g, 'ニャ').replace(/ナ/g, 'ニャ') + .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') // en-US .replace(/(?<=n)a/gi, x => x === 'A' ? 'YA' : 'ya') .replace(/(?<=morn)ing/gi, x => x === 'ING' ? 'YAN' : 'yan') .replace(/(?<=every)one/gi, x => x === 'ONE' ? 'NYAN' : 'nyan') // ko-KR .replace(/[나-낳]/g, match => String.fromCharCode( - match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0) + match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), )) .replace(/(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm, '다냥') .replace(/(야(?=\?))|(야$)|(야(?= ))/gm, '냥'); diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index fdecc278d4..7aeb65f296 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -132,11 +132,27 @@ type NullOrUndefined

= // https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection // Get intersection from union type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +type PartialIntersection = Partial>; // https://github.com/misskey-dev/misskey/pull/8144#discussion_r785287552 // To get union, we use `Foo extends any ? Hoge : never` type UnionSchemaType = X extends any ? SchemaType : never; -type ArrayUnion = T extends any ? Array : never; +type UnionObjectSchemaType = X extends any ? ObjectSchemaType : never; +type ArrayUnion = T extends any ? Array : never; + +type ObjectSchemaTypeDef

= + p['ref'] extends keyof typeof refs ? Packed : + p['properties'] extends NonNullable ? + p['anyOf'] extends ReadonlyArray ? + ObjType[number]> & UnionObjectSchemaType & PartialIntersection> + : + ObjType[number]> + : + p['anyOf'] extends ReadonlyArray ? UnionObjectSchemaType & PartialIntersection> : + p['allOf'] extends ReadonlyArray ? UnionToIntersection> : + any + +type ObjectSchemaType

= NullOrUndefined>; export type SchemaTypeDef

= p['type'] extends 'null' ? null : @@ -149,13 +165,7 @@ export type SchemaTypeDef

= string ) : p['type'] extends 'boolean' ? boolean : - p['type'] extends 'object' ? ( - p['ref'] extends keyof typeof refs ? Packed : - p['properties'] extends NonNullable ? ObjType[number]> : - p['anyOf'] extends ReadonlyArray ? UnionSchemaType & Partial>> : - p['allOf'] extends ReadonlyArray ? UnionToIntersection> : - any - ) : + p['type'] extends 'object' ? ObjectSchemaTypeDef

: p['type'] extends 'array' ? ( p['items'] extends OfSchema ? ( p['items']['anyOf'] extends ReadonlyArray ? UnionSchemaType>[] : @@ -166,6 +176,7 @@ export type SchemaTypeDef

= p['items'] extends NonNullable ? SchemaTypeDef[] : any[] ) : + p['anyOf'] extends ReadonlyArray ? UnionSchemaType & PartialIntersection> : p['oneOf'] extends ReadonlyArray ? UnionSchemaType : any; diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts index 5d222a6da1..9d777c6236 100644 --- a/packages/backend/src/models/entities/Meta.ts +++ b/packages/backend/src/models/entities/Meta.ts @@ -279,57 +279,6 @@ export class Meta { }) public swPrivateKey: string | null; - @Column('boolean', { - default: false, - }) - public enableTwitterIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerKey: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public twitterConsumerSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableGithubIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public githubClientSecret: string | null; - - @Column('boolean', { - default: false, - }) - public enableDiscordIntegration: boolean; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientId: string | null; - - @Column('varchar', { - length: 128, - nullable: true, - }) - public discordClientSecret: string | null; - @Column('varchar', { length: 128, nullable: true, diff --git a/packages/backend/src/models/entities/UserProfile.ts b/packages/backend/src/models/entities/UserProfile.ts index 86df8d5d98..1ff261cda3 100644 --- a/packages/backend/src/models/entities/UserProfile.ts +++ b/packages/backend/src/models/entities/UserProfile.ts @@ -184,11 +184,6 @@ export class UserProfile { @JoinColumn() public pinnedPage: Page | null; - @Column('jsonb', { - default: {}, - }) - public integrations: Record; - @Index() @Column('boolean', { default: false, select: false, diff --git a/packages/backend/src/models/schema/user.ts b/packages/backend/src/models/schema/user.ts index aac5e9332c..1fc9352539 100644 --- a/packages/backend/src/models/schema/user.ts +++ b/packages/backend/src/models/schema/user.ts @@ -323,10 +323,6 @@ export const packedMeDetailedOnlySchema = { type: 'boolean', nullable: false, optional: false, }, - integrations: { - type: 'object', - nullable: true, optional: false, - }, mutedWords: { type: 'array', nullable: false, optional: false, diff --git a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts index 2adf7cbe6d..5254d3c7d8 100644 --- a/packages/backend/src/queue/processors/CleanChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/CleanChartsProcessorService.ts @@ -12,7 +12,6 @@ import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; -import HashtagChart from '@/core/chart/charts/hashtag.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; @@ -37,7 +36,6 @@ export class CleanChartsProcessorService { private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, - private hashtagChart: HashtagChart, private perUserFollowingChart: PerUserFollowingChart, private perUserDriveChart: PerUserDriveChart, private apRequestChart: ApRequestChart, @@ -61,7 +59,6 @@ export class CleanChartsProcessorService { this.perUserPvChart.clean(), this.driveChart.clean(), this.perUserReactionsChart.clean(), - this.hashtagChart.clean(), this.perUserFollowingChart.clean(), this.perUserDriveChart.clean(), this.apRequestChart.clean(), diff --git a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts index 1a8fe65a4f..74e7c632d5 100644 --- a/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/ResyncChartsProcessorService.ts @@ -11,13 +11,12 @@ import InstanceChart from '@/core/chart/charts/instance.js'; import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; -import HashtagChart from '@/core/chart/charts/hashtag.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ResyncChartsProcessorService { @@ -35,7 +34,6 @@ export class ResyncChartsProcessorService { private perUserNotesChart: PerUserNotesChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, - private hashtagChart: HashtagChart, private perUserFollowingChart: PerUserFollowingChart, private perUserDriveChart: PerUserDriveChart, private apRequestChart: ApRequestChart, diff --git a/packages/backend/src/queue/processors/TickChartsProcessorService.ts b/packages/backend/src/queue/processors/TickChartsProcessorService.ts index 51eff2a155..751e02dc20 100644 --- a/packages/backend/src/queue/processors/TickChartsProcessorService.ts +++ b/packages/backend/src/queue/processors/TickChartsProcessorService.ts @@ -12,7 +12,6 @@ import PerUserNotesChart from '@/core/chart/charts/per-user-notes.js'; import PerUserPvChart from '@/core/chart/charts/per-user-pv.js'; import DriveChart from '@/core/chart/charts/drive.js'; import PerUserReactionsChart from '@/core/chart/charts/per-user-reactions.js'; -import HashtagChart from '@/core/chart/charts/hashtag.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import ApRequestChart from '@/core/chart/charts/ap-request.js'; @@ -37,7 +36,6 @@ export class TickChartsProcessorService { private perUserPvChart: PerUserPvChart, private driveChart: DriveChart, private perUserReactionsChart: PerUserReactionsChart, - private hashtagChart: HashtagChart, private perUserFollowingChart: PerUserFollowingChart, private perUserDriveChart: PerUserDriveChart, private apRequestChart: ApRequestChart, @@ -61,7 +59,6 @@ export class TickChartsProcessorService { this.perUserPvChart.tick(false), this.driveChart.tick(false), this.perUserReactionsChart.tick(false), - this.hashtagChart.tick(false), this.perUserFollowingChart.tick(false), this.perUserDriveChart.tick(false), this.apRequestChart.tick(false), diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts index 024ddfe632..a43630c041 100644 --- a/packages/backend/src/server/NodeinfoServerService.ts +++ b/packages/backend/src/server/NodeinfoServerService.ts @@ -111,9 +111,6 @@ export class NodeinfoServerService { enableHcaptcha: meta.enableHcaptcha, enableRecaptcha: meta.enableRecaptcha, maxNoteTextLength: MAX_NOTE_TEXT_LENGTH, - enableTwitterIntegration: meta.enableTwitterIntegration, - enableGithubIntegration: meta.enableGithubIntegration, - enableDiscordIntegration: meta.enableDiscordIntegration, enableEmail: meta.enableEmail, enableServiceWorker: meta.enableServiceWorker, proxyAccountName: proxyAccount ? proxyAccount.username : null, diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 9dc1527698..b605f3c8ab 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -7,9 +7,6 @@ import { NodeinfoServerService } from './NodeinfoServerService.js'; import { ServerService } from './ServerService.js'; import { WellKnownServerService } from './WellKnownServerService.js'; import { GetterService } from './api/GetterService.js'; -import { DiscordServerService } from './api/integration/DiscordServerService.js'; -import { GithubServerService } from './api/integration/GithubServerService.js'; -import { TwitterServerService } from './api/integration/TwitterServerService.js'; import { ChannelsService } from './api/stream/ChannelsService.js'; import { ActivityPubServerService } from './ActivityPubServerService.js'; import { ApiLoggerService } from './api/ApiLoggerService.js'; @@ -54,9 +51,6 @@ import { UserListChannelService } from './api/stream/channels/user-list.js'; ServerService, WellKnownServerService, GetterService, - DiscordServerService, - GithubServerService, - TwitterServerService, ChannelsService, ApiCallService, ApiLoggerService, diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts index b29c9616cc..e406949cd4 100644 --- a/packages/backend/src/server/api/ApiServerService.ts +++ b/packages/backend/src/server/api/ApiServerService.ts @@ -12,9 +12,6 @@ import endpoints, { IEndpoint } from './endpoints.js'; import { ApiCallService } from './ApiCallService.js'; import { SignupApiService } from './SignupApiService.js'; import { SigninApiService } from './SigninApiService.js'; -import { GithubServerService } from './integration/GithubServerService.js'; -import { DiscordServerService } from './integration/DiscordServerService.js'; -import { TwitterServerService } from './integration/TwitterServerService.js'; import type { FastifyInstance, FastifyPluginOptions } from 'fastify'; @Injectable() @@ -38,9 +35,6 @@ export class ApiServerService { private apiCallService: ApiCallService, private signupApiService: SignupApiService, private signinApiService: SigninApiService, - private githubServerService: GithubServerService, - private discordServerService: DiscordServerService, - private twitterServerService: TwitterServerService, ) { //this.createServer = this.createServer.bind(this); } @@ -133,10 +127,6 @@ export class ApiServerService { fastify.post<{ Body: { code: string; } }>('/signup-pending', (request, reply) => this.signupApiService.signupPending(request, reply)); - fastify.register(this.discordServerService.create); - fastify.register(this.githubServerService.create); - fastify.register(this.twitterServerService.create); - fastify.get('/v1/instance/peers', async (request, reply) => { const instances = await this.instancesRepository.find({ select: ['host'], diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 466651f379..4a55c6cbe3 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -97,7 +97,6 @@ import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; import * as ep___charts_drive from './endpoints/charts/drive.js'; import * as ep___charts_federation from './endpoints/charts/federation.js'; -import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; import * as ep___charts_instance from './endpoints/charts/instance.js'; import * as ep___charts_notes from './endpoints/charts/notes.js'; import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; @@ -433,7 +432,6 @@ const $charts_activeUsers: Provider = { provide: 'ep:charts/active-users', useCl const $charts_apRequest: Provider = { provide: 'ep:charts/ap-request', useClass: ep___charts_apRequest.default }; const $charts_drive: Provider = { provide: 'ep:charts/drive', useClass: ep___charts_drive.default }; const $charts_federation: Provider = { provide: 'ep:charts/federation', useClass: ep___charts_federation.default }; -const $charts_hashtag: Provider = { provide: 'ep:charts/hashtag', useClass: ep___charts_hashtag.default }; const $charts_instance: Provider = { provide: 'ep:charts/instance', useClass: ep___charts_instance.default }; const $charts_notes: Provider = { provide: 'ep:charts/notes', useClass: ep___charts_notes.default }; const $charts_user_drive: Provider = { provide: 'ep:charts/user/drive', useClass: ep___charts_user_drive.default }; @@ -773,7 +771,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $charts_apRequest, $charts_drive, $charts_federation, - $charts_hashtag, $charts_instance, $charts_notes, $charts_user_drive, @@ -1107,7 +1104,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $charts_apRequest, $charts_drive, $charts_federation, - $charts_hashtag, $charts_instance, $charts_notes, $charts_user_drive, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 3678fe14e8..55e1900d51 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -96,7 +96,6 @@ import * as ep___charts_activeUsers from './endpoints/charts/active-users.js'; import * as ep___charts_apRequest from './endpoints/charts/ap-request.js'; import * as ep___charts_drive from './endpoints/charts/drive.js'; import * as ep___charts_federation from './endpoints/charts/federation.js'; -import * as ep___charts_hashtag from './endpoints/charts/hashtag.js'; import * as ep___charts_instance from './endpoints/charts/instance.js'; import * as ep___charts_notes from './endpoints/charts/notes.js'; import * as ep___charts_user_drive from './endpoints/charts/user/drive.js'; @@ -430,7 +429,6 @@ const eps = [ ['charts/ap-request', ep___charts_apRequest], ['charts/drive', ep___charts_drive], ['charts/federation', ep___charts_federation], - ['charts/hashtag', ep___charts_hashtag], ['charts/instance', ep___charts_instance], ['charts/notes', ep___charts_notes], ['charts/user/drive', ep___charts_user_drive], diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index b393827054..2b19104ea7 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -138,18 +138,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - enableTwitterIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, - enableGithubIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, - enableDiscordIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, enableServiceWorker: { type: 'boolean', optional: false, nullable: false, @@ -223,30 +211,6 @@ export const meta = { optional: true, nullable: true, format: 'id', }, - twitterConsumerKey: { - type: 'string', - optional: true, nullable: true, - }, - twitterConsumerSecret: { - type: 'string', - optional: true, nullable: true, - }, - githubClientId: { - type: 'string', - optional: true, nullable: true, - }, - githubClientSecret: { - type: 'string', - optional: true, nullable: true, - }, - discordClientId: { - type: 'string', - optional: true, nullable: true, - }, - discordClientSecret: { - type: 'string', - optional: true, nullable: true, - }, summaryProxy: { type: 'string', optional: true, nullable: true, @@ -389,9 +353,6 @@ export default class extends Endpoint { defaultLightTheme: instance.defaultLightTheme, defaultDarkTheme: instance.defaultDarkTheme, enableEmail: instance.enableEmail, - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, enableServiceWorker: instance.enableServiceWorker, translatorAvailable: instance.deeplAuthKey != null, pinnedPages: instance.pinnedPages, @@ -409,12 +370,6 @@ export default class extends Endpoint { setSensitiveFlagAutomatically: instance.setSensitiveFlagAutomatically, enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos, proxyAccountId: instance.proxyAccountId, - twitterConsumerKey: instance.twitterConsumerKey, - twitterConsumerSecret: instance.twitterConsumerSecret, - githubClientId: instance.githubClientId, - githubClientSecret: instance.githubClientSecret, - discordClientId: instance.discordClientId, - discordClientSecret: instance.discordClientSecret, summalyProxy: instance.summalyProxy, email: instance.email, smtpSecure: instance.smtpSecure, diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index 94603cc91a..823af6d8be 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -65,11 +65,6 @@ export default class extends Endpoint { }; } - const maskedKeys = ['accessToken', 'accessTokenSecret', 'refreshToken']; - Object.keys(profile.integrations).forEach(integration => { - maskedKeys.forEach(key => profile.integrations[integration][key] = ''); - }); - const signins = await this.signinsRepository.findBy({ userId: user.id }); const roles = await this.roleService.getUserRoles(user.id); @@ -84,7 +79,6 @@ export default class extends Endpoint { carefulBot: profile.carefulBot, injectFeaturedNote: profile.injectFeaturedNote, receiveAnnouncementEmail: profile.receiveAnnouncementEmail, - integrations: profile.integrations, mutedWords: profile.mutedWords, mutedInstances: profile.mutedInstances, mutingNotificationTypes: profile.mutingNotificationTypes, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index aacd634ed8..354ef22aa7 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -68,15 +68,6 @@ export const paramDef = { summalyProxy: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, - enableTwitterIntegration: { type: 'boolean' }, - twitterConsumerKey: { type: 'string', nullable: true }, - twitterConsumerSecret: { type: 'string', nullable: true }, - enableGithubIntegration: { type: 'boolean' }, - githubClientId: { type: 'string', nullable: true }, - githubClientSecret: { type: 'string', nullable: true }, - enableDiscordIntegration: { type: 'boolean' }, - discordClientId: { type: 'string', nullable: true }, - discordClientSecret: { type: 'string', nullable: true }, enableEmail: { type: 'boolean' }, email: { type: 'string', nullable: true }, smtpSecure: { type: 'boolean' }, @@ -270,42 +261,6 @@ export default class extends Endpoint { set.summalyProxy = ps.summalyProxy; } - if (ps.enableTwitterIntegration !== undefined) { - set.enableTwitterIntegration = ps.enableTwitterIntegration; - } - - if (ps.twitterConsumerKey !== undefined) { - set.twitterConsumerKey = ps.twitterConsumerKey; - } - - if (ps.twitterConsumerSecret !== undefined) { - set.twitterConsumerSecret = ps.twitterConsumerSecret; - } - - if (ps.enableGithubIntegration !== undefined) { - set.enableGithubIntegration = ps.enableGithubIntegration; - } - - if (ps.githubClientId !== undefined) { - set.githubClientId = ps.githubClientId; - } - - if (ps.githubClientSecret !== undefined) { - set.githubClientSecret = ps.githubClientSecret; - } - - if (ps.enableDiscordIntegration !== undefined) { - set.enableDiscordIntegration = ps.enableDiscordIntegration; - } - - if (ps.discordClientId !== undefined) { - set.discordClientId = ps.discordClientId; - } - - if (ps.discordClientSecret !== undefined) { - set.discordClientSecret = ps.discordClientSecret; - } - if (ps.enableEmail !== undefined) { set.enableEmail = ps.enableEmail; } diff --git a/packages/backend/src/server/api/endpoints/charts/hashtag.ts b/packages/backend/src/server/api/endpoints/charts/hashtag.ts deleted file mode 100644 index 71e5bab766..0000000000 --- a/packages/backend/src/server/api/endpoints/charts/hashtag.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { getJsonSchema } from '@/core/chart/core.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import HashtagChart from '@/core/chart/charts/hashtag.js'; -import { schema } from '@/core/chart/charts/entities/hashtag.js'; - -export const meta = { - tags: ['charts', 'hashtags'], - - res: getJsonSchema(schema), - - allowGet: true, - cacheSec: 60 * 60, -} as const; - -export const paramDef = { - type: 'object', - properties: { - span: { type: 'string', enum: ['day', 'hour'] }, - limit: { type: 'integer', minimum: 1, maximum: 500, default: 30 }, - offset: { type: 'integer', nullable: true, default: null }, - tag: { type: 'string' }, - }, - required: ['span', 'tag'], -} as const; - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint { - constructor( - private hashtagChart: HashtagChart, - ) { - super(meta, paramDef, async (ps, me) => { - return await this.hashtagChart.getChart(ps.span, ps.limit, ps.offset ? new Date(ps.offset) : null, ps.tag); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts index 89fa503173..3baf945323 100644 --- a/packages/backend/src/server/api/endpoints/meta.ts +++ b/packages/backend/src/server/api/endpoints/meta.ts @@ -169,18 +169,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - enableTwitterIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, - enableGithubIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, - enableDiscordIntegration: { - type: 'boolean', - optional: false, nullable: false, - }, enableServiceWorker: { type: 'boolean', optional: false, nullable: false, @@ -225,18 +213,6 @@ export const meta = { type: 'boolean', optional: false, nullable: false, }, - twitter: { - type: 'boolean', - optional: false, nullable: false, - }, - github: { - type: 'boolean', - optional: false, nullable: false, - }, - discord: { - type: 'boolean', - optional: false, nullable: false, - }, serviceWorker: { type: 'boolean', optional: false, nullable: false, @@ -325,11 +301,6 @@ export default class extends Endpoint { imageUrl: ad.imageUrl, })), enableEmail: instance.enableEmail, - - enableTwitterIntegration: instance.enableTwitterIntegration, - enableGithubIntegration: instance.enableGithubIntegration, - enableDiscordIntegration: instance.enableDiscordIntegration, - enableServiceWorker: instance.enableServiceWorker, translatorAvailable: instance.deeplAuthKey != null, @@ -358,9 +329,6 @@ export default class extends Endpoint { recaptcha: instance.enableRecaptcha, turnstile: instance.enableTurnstile, objectStorage: instance.useObjectStorage, - twitter: instance.enableTwitterIntegration, - github: instance.enableGithubIntegration, - discord: instance.enableDiscordIntegration, serviceWorker: instance.enableServiceWorker, miauth: true, }; diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 92bc8a7595..a709ab2f7a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -90,48 +90,13 @@ export const paramDef = { visibleUserIds: { type: 'array', uniqueItems: true, items: { type: 'string', format: 'misskey:id', } }, - text: { type: 'string', maxLength: MAX_NOTE_TEXT_LENGTH, nullable: true }, cw: { type: 'string', nullable: true, maxLength: 100 }, localOnly: { type: 'boolean', default: false }, noExtractMentions: { type: 'boolean', default: false }, noExtractHashtags: { type: 'boolean', default: false }, noExtractEmojis: { type: 'boolean', default: false }, - fileIds: { - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, - mediaIds: { - deprecated: true, - description: 'Use `fileIds` instead. If both are specified, this property is discarded.', - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, replyId: { type: 'string', format: 'misskey:id', nullable: true }, - renoteId: { type: 'string', format: 'misskey:id', nullable: true }, channelId: { type: 'string', format: 'misskey:id', nullable: true }, - poll: { - type: 'object', - nullable: true, - properties: { - choices: { - type: 'array', - uniqueItems: true, - minItems: 2, - maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, - }, - multiple: { type: 'boolean', default: false }, - expiresAt: { type: 'integer', nullable: true }, - expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, - }, - required: ['choices'], - }, }, anyOf: [ { @@ -143,21 +108,60 @@ export const paramDef = { }, { // (re)note with files, text and poll are optional + properties: { + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + }, required: ['fileIds'], }, { // (re)note with files, text and poll are optional + properties: { + 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' }, + }, + }, required: ['mediaIds'], }, { // (re)note with poll, text and files are optional properties: { - poll: { type: 'object', nullable: false }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, }, required: ['poll'], }, { // pure renote + properties: { + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + }, required: ['renoteId'], }, ], diff --git a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts index 95491211bc..b176e6c65d 100644 --- a/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts +++ b/packages/backend/src/server/api/endpoints/users/search-by-username-and-host.ts @@ -29,14 +29,22 @@ export const meta = { export const paramDef = { type: 'object', properties: { - username: { type: 'string', nullable: true }, - host: { type: 'string', nullable: true }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, detail: { type: 'boolean', default: true }, }, anyOf: [ - { required: ['username'] }, - { required: ['host'] }, + { + properties: { + username: { type: 'string', nullable: true }, + }, + required: ['username'] + }, + { + properties: { + host: { type: 'string', nullable: true }, + }, + required: ['host'] + }, ], } as const; diff --git a/packages/backend/src/server/api/integration/DiscordServerService.ts b/packages/backend/src/server/api/integration/DiscordServerService.ts deleted file mode 100644 index 0ac2733817..0000000000 --- a/packages/backend/src/server/api/integration/DiscordServerService.ts +++ /dev/null @@ -1,308 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class DiscordServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/discord', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.discord; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'Discordの連携を解除しました :v:'; - }); - - const getOAuth2 = async () => { - const meta = await this.metaService.fetch(true); - - if (meta.enableDiscordIntegration) { - return new OAuth2( - meta.discordClientId!, - meta.discordClientSecret!, - 'https://discord.com/', - 'api/oauth2/authorize', - 'api/oauth2/token'); - } else { - return null; - } - }; - - fastify.get('/connect/discord', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const params = { - redirect_uri: `${this.config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - this.redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/signin/discord', async (request, reply) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${this.config.url}/api/dc/cb`, - scope: ['identify'], - state: uuid(), - response_type: 'code', - }; - - reply.setCookie('signin_with_discord_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - this.redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOAuth2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/dc/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const oauth2 = await getOAuth2(); - - if (!userToken) { - const sessid = request.cookies['signin_with_discord_sid']; - - if (!sessid) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const profile = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'discord\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (profile == null) { - throw new FastifyReplyError(404, `@${username}#${discriminator}と連携しているMisskeyアカウントはありませんでした...`); - } - - await this.userProfilesRepository.update(profile.userId, { - integrations: { - ...profile.integrations, - discord: { - id: id, - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - username: username, - discriminator: discriminator, - }, - }, - }); - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: profile.userId }) as ILocalUser, true); - } else { - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken, refreshToken, expiresDate } = await new Promise((res, rej) => - oauth2!.getOAuthAccessToken(code, { - grant_type: 'authorization_code', - redirect_uri, - }, (err, accessToken, refreshToken, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ - accessToken, - refreshToken, - expiresDate: Date.now() + Number(result.expires_in) * 1000, - }); - } - })); - - const { id, username, discriminator } = (await this.httpRequestService.getJson('https://discord.com/api/users/@me', '*/*', { - 'Authorization': `Bearer ${accessToken}`, - })) as Record; - if (typeof id !== 'string' || typeof username !== 'string' || typeof discriminator !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - discord: { - accessToken: accessToken, - refreshToken: refreshToken, - expiresDate: expiresDate, - id: id, - username: username, - discriminator: discriminator, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `Discord: @${username}#${discriminator} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/backend/src/server/api/integration/GithubServerService.ts b/packages/backend/src/server/api/integration/GithubServerService.ts deleted file mode 100644 index a8c745d2dc..0000000000 --- a/packages/backend/src/server/api/integration/GithubServerService.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { OAuth2 } from 'oauth'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class GithubServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/github', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.github; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'GitHubの連携を解除しました :v:'; - }); - - const getOath2 = async () => { - const meta = await this.metaService.fetch(true); - - if (meta.enableGithubIntegration && meta.githubClientId && meta.githubClientSecret) { - return new OAuth2( - meta.githubClientId, - meta.githubClientSecret, - 'https://github.com/', - 'login/oauth/authorize', - 'login/oauth/access_token'); - } else { - return null; - } - }; - - fastify.get('/connect/github', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (!userToken) { - throw new FastifyReplyError(400, 'signin required'); - } - - const params = { - redirect_uri: `${this.config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - this.redisClient.set(userToken, JSON.stringify(params)); - - const oauth2 = await getOath2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/signin/github', async (request, reply) => { - const sessid = uuid(); - - const params = { - redirect_uri: `${this.config.url}/api/gh/cb`, - scope: ['read:user'], - state: uuid(), - }; - - reply.setCookie('signin_with_github_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - this.redisClient.set(sessid, JSON.stringify(params)); - - const oauth2 = await getOath2(); - reply.redirect(oauth2!.getAuthorizeUrl(params)); - }); - - fastify.get('/gh/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const oauth2 = await getOath2(); - - if (!userToken) { - const sessid = request.cookies['signin_with_github_sid']; - - if (!sessid) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken } = await new Promise<{ accessToken: string }>((res, rej) => - oauth2!.getOAuthAccessToken(code, { - redirect_uri, - }, (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - })); - - const { login, id } = (await this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - if (typeof login !== 'string' || typeof id !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const link = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'github\'->>\'id\' = :id', { id: id }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - throw new FastifyReplyError(404, `@${login}と連携しているMisskeyアカウントはありませんでした...`); - } - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const code = request.query.code; - - if (!code || typeof code !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { redirect_uri, state } = await new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, state) => { - if (state == null) throw new Error('empty state'); - res(JSON.parse(state)); - }); - }); - - if (request.query.state !== state) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const { accessToken } = await new Promise<{ accessToken: string }>((res, rej) => - oauth2!.getOAuthAccessToken( - code, - { redirect_uri }, - (err, accessToken, refresh, result) => { - if (err) { - rej(err); - } else if (result.error) { - rej(result.error); - } else { - res({ accessToken }); - } - })); - - const { login, id } = (await this.httpRequestService.getJson('https://api.github.com/user', 'application/vnd.github.v3+json', { - 'Authorization': `bearer ${accessToken}`, - })) as Record; - - if (typeof login !== 'string' || typeof id !== 'number') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - github: { - accessToken: accessToken, - id: id, - login: login, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `GitHub: @${login} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/backend/src/server/api/integration/TwitterServerService.ts b/packages/backend/src/server/api/integration/TwitterServerService.ts deleted file mode 100644 index f31a788d31..0000000000 --- a/packages/backend/src/server/api/integration/TwitterServerService.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import Redis from 'ioredis'; -import { v4 as uuid } from 'uuid'; -import { IsNull } from 'typeorm'; -import * as autwh from 'autwh'; -import type { Config } from '@/config.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/index.js'; -import { DI } from '@/di-symbols.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import type { ILocalUser } from '@/models/entities/User.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { FastifyReplyError } from '@/misc/fastify-reply-error.js'; -import { bindThis } from '@/decorators.js'; -import { SigninService } from '../SigninService.js'; -import type { FastifyInstance, FastifyRequest, FastifyPluginOptions } from 'fastify'; - -@Injectable() -export class TwitterServerService { - constructor( - @Inject(DI.config) - private config: Config, - - @Inject(DI.redis) - private redisClient: Redis.Redis, - - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - - private userEntityService: UserEntityService, - private httpRequestService: HttpRequestService, - private globalEventService: GlobalEventService, - private metaService: MetaService, - private signinService: SigninService, - ) { - //this.create = this.create.bind(this); - } - - @bindThis - public create(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) { - fastify.get('/disconnect/twitter', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (userToken == null) { - throw new FastifyReplyError(400, 'signin required'); - } - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - delete profile.integrations.twitter; - - await this.userProfilesRepository.update(user.id, { - integrations: profile.integrations, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return 'Twitterの連携を解除しました :v:'; - }); - - const getTwAuth = async () => { - const meta = await this.metaService.fetch(true); - - if (meta.enableTwitterIntegration && meta.twitterConsumerKey && meta.twitterConsumerSecret) { - return autwh({ - consumerKey: meta.twitterConsumerKey, - consumerSecret: meta.twitterConsumerSecret, - callbackUrl: `${this.config.url}/api/tw/cb`, - }); - } else { - return null; - } - }; - - fastify.get('/connect/twitter', async (request, reply) => { - if (!this.compareOrigin(request)) { - throw new FastifyReplyError(400, 'invalid origin'); - } - - const userToken = this.getUserToken(request); - if (userToken == null) { - throw new FastifyReplyError(400, 'signin required'); - } - - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - this.redisClient.set(userToken, JSON.stringify(twCtx)); - reply.redirect(twCtx.url); - }); - - fastify.get('/signin/twitter', async (request, reply) => { - const twAuth = await getTwAuth(); - const twCtx = await twAuth!.begin(); - - const sessid = uuid(); - - this.redisClient.set(sessid, JSON.stringify(twCtx)); - - reply.setCookie('signin_with_twitter_sid', sessid, { - path: '/', - secure: this.config.url.startsWith('https'), - httpOnly: true, - }); - - reply.redirect(twCtx.url); - }); - - fastify.get('/tw/cb', async (request, reply) => { - const userToken = this.getUserToken(request); - - const twAuth = await getTwAuth(); - - if (userToken == null) { - const sessid = request.cookies['signin_with_twitter_sid']; - - if (sessid == null) { - throw new FastifyReplyError(400, 'invalid session'); - } - - const get = new Promise((res, rej) => { - this.redisClient.get(sessid, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const verifier = request.query.oauth_verifier; - if (!verifier || typeof verifier !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const link = await this.userProfilesRepository.createQueryBuilder() - .where('"integrations"->\'twitter\'->>\'userId\' = :id', { id: result.userId }) - .andWhere('"userHost" IS NULL') - .getOne(); - - if (link == null) { - throw new FastifyReplyError(404, `@${result.screenName}と連携しているMisskeyアカウントはありませんでした...`); - } - - return this.signinService.signin(request, reply, await this.usersRepository.findOneBy({ id: link.userId }) as ILocalUser, true); - } else { - const verifier = request.query.oauth_verifier; - - if (!verifier || typeof verifier !== 'string') { - throw new FastifyReplyError(400, 'invalid session'); - } - - const get = new Promise((res, rej) => { - this.redisClient.get(userToken, async (_, twCtx) => { - res(twCtx); - }); - }); - - const twCtx = await get; - - const result = await twAuth!.done(JSON.parse(twCtx), verifier); - - const user = await this.usersRepository.findOneByOrFail({ - host: IsNull(), - token: userToken, - }); - - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - - await this.userProfilesRepository.update(user.id, { - integrations: { - ...profile.integrations, - twitter: { - accessToken: result.accessToken, - accessTokenSecret: result.accessTokenSecret, - userId: result.userId, - screenName: result.screenName, - }, - }, - }); - - // Publish i updated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', await this.userEntityService.pack(user, user, { - detail: true, - includeSecrets: true, - })); - - return `Twitter: @${result.screenName} を、Misskey: @${user.username} に接続しました!`; - } - }); - - done(); - } - - @bindThis - private getUserToken(request: FastifyRequest): string | null { - return ((request.headers['cookie'] ?? '').match(/igi=(\w+)/) ?? [null, null])[1]; - } - - @bindThis - private compareOrigin(request: FastifyRequest): boolean { - function normalizeUrl(url?: string): string { - return url ? url.endsWith('/') ? url.substr(0, url.length - 1) : url : ''; - } - - const referer = request.headers['referer']; - - return (normalizeUrl(referer) === normalizeUrl(this.config.url)); - } -} diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts index fc145cf0ee..36bfa78363 100644 --- a/packages/backend/src/server/api/stream/types.ts +++ b/packages/backend/src/server/api/stream/types.ts @@ -18,31 +18,26 @@ import { Following, Role, RoleAssignment } from '@/models'; import type Emitter from 'strict-event-emitter-types'; import type { EventEmitter } from 'events'; -// redis通すとDateのインスタンスはstringに変換されるので -type Serialized = { - [K in keyof T]: T[K] extends Date ? string : T[K]; -}; - //#region Stream type-body definitions export interface InternalStreamTypes { - userChangeSuspendedState: Serialized<{ id: User['id']; isSuspended: User['isSuspended']; }>; - userTokenRegenerated: Serialized<{ id: User['id']; oldToken: User['token']; newToken: User['token']; }>; - remoteUserUpdated: Serialized<{ id: User['id']; }>; - follow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - unfollow: Serialized<{ followerId: User['id']; followeeId: User['id']; }>; - policiesUpdated: Serialized; - roleCreated: Serialized; - roleDeleted: Serialized; - roleUpdated: Serialized; - userRoleAssigned: Serialized; - userRoleUnassigned: Serialized; - webhookCreated: Serialized; - webhookDeleted: Serialized; - webhookUpdated: Serialized; - antennaCreated: Serialized; - antennaDeleted: Serialized; - antennaUpdated: Serialized; - metaUpdated: Serialized; + userChangeSuspendedState: { id: User['id']; isSuspended: User['isSuspended']; }; + userTokenRegenerated: { id: User['id']; oldToken: User['token']; newToken: User['token']; }; + remoteUserUpdated: { id: User['id']; }; + follow: { followerId: User['id']; followeeId: User['id']; }; + unfollow: { followerId: User['id']; followeeId: User['id']; }; + policiesUpdated: Role['policies']; + roleCreated: Role; + roleDeleted: Role; + roleUpdated: Role; + userRoleAssigned: RoleAssignment; + userRoleUnassigned: RoleAssignment; + webhookCreated: Webhook; + webhookDeleted: Webhook; + webhookUpdated: Webhook; + antennaCreated: Antenna; + antennaDeleted: Antenna; + antennaUpdated: Antenna; + metaUpdated: Meta; } export interface BroadcastTypes { @@ -210,63 +205,72 @@ type EventUnionFromDictionary< U = Events > = U[keyof U]; +// redis通すとDateのインスタンスはstringに変換されるので +type Serialized = { + [K in keyof T]: T[K] extends Date ? string : T[K] extends Record ? Serialized : T[K]; +}; + +type SerializedAll = { + [K in keyof T]: Serialized; +}; + // name/messages(spec) pairs dictionary export type StreamMessages = { internal: { name: 'internal'; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; broadcast: { name: 'broadcast'; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; user: { name: `user:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; main: { name: `mainStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; drive: { name: `driveStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; note: { name: `noteStream:${Note['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; channel: { name: `channelStream:${Channel['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; userList: { name: `userListStream:${UserList['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; antenna: { name: `antennaStream:${Antenna['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; messaging: { name: `messagingStream:${User['id']}-${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; groupMessaging: { name: `messagingStream:${UserGroup['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; messagingIndex: { name: `messagingIndexStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; admin: { name: `adminStream:${User['id']}`; - payload: EventUnionFromDictionary; + payload: EventUnionFromDictionary>; }; notes: { name: 'notesStream'; - payload: Packed<'Note'>; + payload: Serialized>; }; }; diff --git a/packages/backend/test/_e2e/api-visibility.ts b/packages/backend/test/_e2e/api-visibility.ts index 9c21840844..d29b9acb3d 100644 --- a/packages/backend/test/_e2e/api-visibility.ts +++ b/packages/backend/test/_e2e/api-visibility.ts @@ -100,90 +100,90 @@ describe('API visibility', () => { //#region show post // public - it('[show] public-postを自分が見れる', async () => { + test('[show] public-postを自分が見れる', async () => { const res = await show(pub.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postをフォロワーが見れる', async () => { + test('[show] public-postをフォロワーが見れる', async () => { const res = await show(pub.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postを非フォロワーが見れる', async () => { + test('[show] public-postを非フォロワーが見れる', async () => { const res = await show(pub.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-postを未認証が見れる', async () => { + test('[show] public-postを未認証が見れる', async () => { const res = await show(pub.id, null); assert.strictEqual(res.body.text, 'x'); }); // home - it('[show] home-postを自分が見れる', async () => { + test('[show] home-postを自分が見れる', async () => { const res = await show(home.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postをフォロワーが見れる', async () => { + test('[show] home-postをフォロワーが見れる', async () => { const res = await show(home.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postを非フォロワーが見れる', async () => { + test('[show] home-postを非フォロワーが見れる', async () => { const res = await show(home.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-postを未認証が見れる', async () => { + test('[show] home-postを未認証が見れる', async () => { const res = await show(home.id, null); assert.strictEqual(res.body.text, 'x'); }); // followers - it('[show] followers-postを自分が見れる', async () => { + test('[show] followers-postを自分が見れる', async () => { const res = await show(fol.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-postをフォロワーが見れる', async () => { + test('[show] followers-postをフォロワーが見れる', async () => { const res = await show(fol.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-postを非フォロワーが見れない', async () => { + test('[show] followers-postを非フォロワーが見れない', async () => { const res = await show(fol.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-postを未認証が見れない', async () => { + test('[show] followers-postを未認証が見れない', async () => { const res = await show(fol.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-postを自分が見れる', async () => { + test('[show] specified-postを自分が見れる', async () => { const res = await show(spe.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-postを指定ユーザーが見れる', async () => { + test('[show] specified-postを指定ユーザーが見れる', async () => { const res = await show(spe.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-postをフォロワーが見れない', async () => { + test('[show] specified-postをフォロワーが見れない', async () => { const res = await show(spe.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-postを非フォロワーが見れない', async () => { + test('[show] specified-postを非フォロワーが見れない', async () => { const res = await show(spe.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-postを未認証が見れない', async () => { + test('[show] specified-postを未認証が見れない', async () => { const res = await show(spe.id, null); assert.strictEqual(res.body.isHidden, true); }); @@ -191,110 +191,110 @@ describe('API visibility', () => { //#region show reply // public - it('[show] public-replyを自分が見れる', async () => { + test('[show] public-replyを自分が見れる', async () => { const res = await show(pubR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyをされた人が見れる', async () => { + test('[show] public-replyをされた人が見れる', async () => { const res = await show(pubR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyをフォロワーが見れる', async () => { + test('[show] public-replyをフォロワーが見れる', async () => { const res = await show(pubR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyを非フォロワーが見れる', async () => { + test('[show] public-replyを非フォロワーが見れる', async () => { const res = await show(pubR.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] public-replyを未認証が見れる', async () => { + test('[show] public-replyを未認証が見れる', async () => { const res = await show(pubR.id, null); assert.strictEqual(res.body.text, 'x'); }); // home - it('[show] home-replyを自分が見れる', async () => { + test('[show] home-replyを自分が見れる', async () => { const res = await show(homeR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyをされた人が見れる', async () => { + test('[show] home-replyをされた人が見れる', async () => { const res = await show(homeR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyをフォロワーが見れる', async () => { + test('[show] home-replyをフォロワーが見れる', async () => { const res = await show(homeR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyを非フォロワーが見れる', async () => { + test('[show] home-replyを非フォロワーが見れる', async () => { const res = await show(homeR.id, other); assert.strictEqual(res.body.text, 'x'); }); - it('[show] home-replyを未認証が見れる', async () => { + test('[show] home-replyを未認証が見れる', async () => { const res = await show(homeR.id, null); assert.strictEqual(res.body.text, 'x'); }); // followers - it('[show] followers-replyを自分が見れる', async () => { + test('[show] followers-replyを自分が見れる', async () => { const res = await show(folR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async () => { + test('[show] followers-replyを非フォロワーでもリプライされていれば見れる', async () => { const res = await show(folR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyをフォロワーが見れる', async () => { + test('[show] followers-replyをフォロワーが見れる', async () => { const res = await show(folR.id, follower); assert.strictEqual(res.body.text, 'x'); }); - it('[show] followers-replyを非フォロワーが見れない', async () => { + test('[show] followers-replyを非フォロワーが見れない', async () => { const res = await show(folR.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-replyを未認証が見れない', async () => { + test('[show] followers-replyを未認証が見れない', async () => { const res = await show(folR.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-replyを自分が見れる', async () => { + test('[show] specified-replyを自分が見れる', async () => { const res = await show(speR.id, alice); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyを指定ユーザーが見れる', async () => { + test('[show] specified-replyを指定ユーザーが見れる', async () => { const res = await show(speR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyをされた人が指定されてなくても見れる', async () => { + test('[show] specified-replyをされた人が指定されてなくても見れる', async () => { const res = await show(speR.id, target); assert.strictEqual(res.body.text, 'x'); }); - it('[show] specified-replyをフォロワーが見れない', async () => { + test('[show] specified-replyをフォロワーが見れない', async () => { const res = await show(speR.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-replyを非フォロワーが見れない', async () => { + test('[show] specified-replyを非フォロワーが見れない', async () => { const res = await show(speR.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-replyを未認証が見れない', async () => { + test('[show] specified-replyを未認証が見れない', async () => { const res = await show(speR.id, null); assert.strictEqual(res.body.isHidden, true); }); @@ -302,131 +302,131 @@ describe('API visibility', () => { //#region show mention // public - it('[show] public-mentionを自分が見れる', async () => { + test('[show] public-mentionを自分が見れる', async () => { const res = await show(pubM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionをされた人が見れる', async () => { + test('[show] public-mentionをされた人が見れる', async () => { const res = await show(pubM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionをフォロワーが見れる', async () => { + test('[show] public-mentionをフォロワーが見れる', async () => { const res = await show(pubM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionを非フォロワーが見れる', async () => { + test('[show] public-mentionを非フォロワーが見れる', async () => { const res = await show(pubM.id, other); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] public-mentionを未認証が見れる', async () => { + test('[show] public-mentionを未認証が見れる', async () => { const res = await show(pubM.id, null); assert.strictEqual(res.body.text, '@target x'); }); // home - it('[show] home-mentionを自分が見れる', async () => { + test('[show] home-mentionを自分が見れる', async () => { const res = await show(homeM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionをされた人が見れる', async () => { + test('[show] home-mentionをされた人が見れる', async () => { const res = await show(homeM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionをフォロワーが見れる', async () => { + test('[show] home-mentionをフォロワーが見れる', async () => { const res = await show(homeM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionを非フォロワーが見れる', async () => { + test('[show] home-mentionを非フォロワーが見れる', async () => { const res = await show(homeM.id, other); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] home-mentionを未認証が見れる', async () => { + test('[show] home-mentionを未認証が見れる', async () => { const res = await show(homeM.id, null); assert.strictEqual(res.body.text, '@target x'); }); // followers - it('[show] followers-mentionを自分が見れる', async () => { + test('[show] followers-mentionを自分が見れる', async () => { const res = await show(folM.id, alice); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async () => { + test('[show] followers-mentionをメンションされていれば非フォロワーでも見れる', async () => { const res = await show(folM.id, target); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionをフォロワーが見れる', async () => { + test('[show] followers-mentionをフォロワーが見れる', async () => { const res = await show(folM.id, follower); assert.strictEqual(res.body.text, '@target x'); }); - it('[show] followers-mentionを非フォロワーが見れない', async () => { + test('[show] followers-mentionを非フォロワーが見れない', async () => { const res = await show(folM.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] followers-mentionを未認証が見れない', async () => { + test('[show] followers-mentionを未認証が見れない', async () => { const res = await show(folM.id, null); assert.strictEqual(res.body.isHidden, true); }); // specified - it('[show] specified-mentionを自分が見れる', async () => { + test('[show] specified-mentionを自分が見れる', async () => { const res = await show(speM.id, alice); assert.strictEqual(res.body.text, '@target2 x'); }); - it('[show] specified-mentionを指定ユーザーが見れる', async () => { + test('[show] specified-mentionを指定ユーザーが見れる', async () => { const res = await show(speM.id, target); assert.strictEqual(res.body.text, '@target2 x'); }); - it('[show] specified-mentionをされた人が指定されてなかったら見れない', async () => { + test('[show] specified-mentionをされた人が指定されてなかったら見れない', async () => { const res = await show(speM.id, target2); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionをフォロワーが見れない', async () => { + test('[show] specified-mentionをフォロワーが見れない', async () => { const res = await show(speM.id, follower); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionを非フォロワーが見れない', async () => { + test('[show] specified-mentionを非フォロワーが見れない', async () => { const res = await show(speM.id, other); assert.strictEqual(res.body.isHidden, true); }); - it('[show] specified-mentionを未認証が見れない', async () => { + test('[show] specified-mentionを未認証が見れない', async () => { const res = await show(speM.id, null); assert.strictEqual(res.body.isHidden, true); }); //#endregion //#region HTL - it('[HTL] public-post が 自分が見れる', async () => { + test('[HTL] public-post が 自分が見れる', async () => { const res = await request('/notes/timeline', { limit: 100 }, alice); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[HTL] public-post が 非フォロワーから見れない', async () => { + test('[HTL] public-post が 非フォロワーから見れない', async () => { const res = await request('/notes/timeline', { limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === pub.id); assert.strictEqual(notes.length, 0); }); - it('[HTL] followers-post が フォロワーから見れる', async () => { + test('[HTL] followers-post が フォロワーから見れる', async () => { const res = await request('/notes/timeline', { limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === fol.id); @@ -435,21 +435,21 @@ describe('API visibility', () => { //#endregion //#region RTL - it('[replies] followers-reply が フォロワーから見れる', async () => { + test('[replies] followers-reply が フォロワーから見れる', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, follower); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { + test('[replies] followers-reply が 非フォロワー (リプライ先ではない) から見れない', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, other); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes.length, 0); }); - it('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { + test('[replies] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { const res = await request('/notes/replies', { noteId: tgt.id, limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); @@ -458,14 +458,14 @@ describe('API visibility', () => { //#endregion //#region MTL - it('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { + test('[mentions] followers-reply が 非フォロワー (リプライ先である) から見れる', async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folR.id); assert.strictEqual(notes[0].text, 'x'); }); - it('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { + test('[mentions] followers-mention が 非フォロワー (メンション先である) から見れる', async () => { const res = await request('/notes/mentions', { limit: 100 }, target); assert.strictEqual(res.status, 200); const notes = res.body.filter((n: any) => n.id === folM.id); diff --git a/packages/backend/test/_e2e/api.ts b/packages/backend/test/_e2e/api.ts index 3c08022031..7542c34db0 100644 --- a/packages/backend/test/_e2e/api.ts +++ b/packages/backend/test/_e2e/api.ts @@ -22,7 +22,7 @@ describe('API', () => { }); describe('General validation', () => { - it('wrong type', async(async () => { + test('wrong type', async(async () => { const res = await request('/test', { required: true, string: 42, @@ -30,14 +30,14 @@ describe('API', () => { assert.strictEqual(res.status, 400); })); - it('missing require param', async(async () => { + test('missing require param', async(async () => { const res = await request('/test', { string: 'a', }); assert.strictEqual(res.status, 400); })); - it('invalid misskey:id (empty string)', async(async () => { + test('invalid misskey:id (empty string)', async(async () => { const res = await request('/test', { required: true, id: '', @@ -45,7 +45,7 @@ describe('API', () => { assert.strictEqual(res.status, 400); })); - it('valid misskey:id', async(async () => { + test('valid misskey:id', async(async () => { const res = await request('/test', { required: true, id: '8wvhjghbxu', @@ -53,7 +53,7 @@ describe('API', () => { assert.strictEqual(res.status, 200); })); - it('default value', async(async () => { + test('default value', async(async () => { const res = await request('/test', { required: true, string: 'a', @@ -62,7 +62,7 @@ describe('API', () => { assert.strictEqual(res.body.default, 'hello'); })); - it('can set null even if it has default value', async(async () => { + test('can set null even if it has default value', async(async () => { const res = await request('/test', { required: true, nullableDefault: null, @@ -71,7 +71,7 @@ describe('API', () => { assert.strictEqual(res.body.nullableDefault, null); })); - it('cannot set undefined if it has default value', async(async () => { + test('cannot set undefined if it has default value', async(async () => { const res = await request('/test', { required: true, nullableDefault: undefined, diff --git a/packages/backend/test/_e2e/block.ts b/packages/backend/test/_e2e/block.ts index bb31983a32..c5f43e153c 100644 --- a/packages/backend/test/_e2e/block.ts +++ b/packages/backend/test/_e2e/block.ts @@ -23,7 +23,7 @@ describe('Block', () => { await shutdownServer(p); }); - it('Block作成', async () => { + test('Block作成', async () => { const res = await request('/blocking/create', { userId: bob.id, }, alice); @@ -31,14 +31,14 @@ describe('Block', () => { assert.strictEqual(res.status, 200); }); - it('ブロックされているユーザーをフォローできない', async () => { + test('ブロックされているユーザーをフォローできない', async () => { const res = await request('/following/create', { userId: alice.id }, bob); assert.strictEqual(res.status, 400); assert.strictEqual(res.body.error.id, 'c4ab57cc-4e41-45e9-bfd9-584f61e35ce0'); }); - it('ブロックされているユーザーにリアクションできない', async () => { + test('ブロックされているユーザーにリアクションできない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/reactions/create', { noteId: note.id, reaction: '👍' }, bob); @@ -47,7 +47,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.id, '20ef5475-9f38-4e4c-bd33-de6d979498ec'); }); - it('ブロックされているユーザーに返信できない', async () => { + test('ブロックされているユーザーに返信できない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { replyId: note.id, text: 'yo' }, bob); @@ -56,7 +56,7 @@ describe('Block', () => { assert.strictEqual(res.body.error.id, 'b390d7e1-8a5e-46ed-b625-06271cafd3d3'); }); - it('ブロックされているユーザーのノートをRenoteできない', async () => { + test('ブロックされているユーザーのノートをRenoteできない', async () => { const note = await post(alice, { text: 'hello' }); const res = await request('/notes/create', { renoteId: note.id, text: 'yo' }, bob); @@ -69,7 +69,7 @@ describe('Block', () => { // TODO: ユーザーリストから除外されるテスト - it('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async () => { + test('タイムライン(LTL)にブロックされているユーザーの投稿が含まれない', async () => { const aliceNote = await post(alice); const bobNote = await post(bob); const carolNote = await post(carol); diff --git a/packages/backend/test/_e2e/endpoints.ts b/packages/backend/test/_e2e/endpoints.ts index 05b74a65d4..ea8433dfa2 100644 --- a/packages/backend/test/_e2e/endpoints.ts +++ b/packages/backend/test/_e2e/endpoints.ts @@ -22,7 +22,7 @@ describe('Endpoints', () => { }); describe('signup', () => { - it('不正なユーザー名でアカウントが作成できない', async () => { + test('不正なユーザー名でアカウントが作成できない', async () => { const res = await request('api/signup', { username: 'test.', password: 'test', @@ -30,7 +30,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパスワードでアカウントが作成できない', async () => { + test('空のパスワードでアカウントが作成できない', async () => { const res = await request('api/signup', { username: 'test', password: '', @@ -38,7 +38,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('正しくアカウントが作成できる', async () => { + test('正しくアカウントが作成できる', async () => { const me = { username: 'test1', password: 'test1', @@ -51,7 +51,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.username, me.username); }); - it('同じユーザー名のアカウントは作成できない', async () => { + test('同じユーザー名のアカウントは作成できない', async () => { const res = await request('api/signup', { username: 'test1', password: 'test1', @@ -62,7 +62,7 @@ describe('Endpoints', () => { }); describe('signin', () => { - it('間違ったパスワードでサインインできない', async () => { + test('間違ったパスワードでサインインできない', async () => { const res = await request('api/signin', { username: 'test1', password: 'bar', @@ -71,7 +71,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 403); }); - it('クエリをインジェクションできない', async () => { + test('クエリをインジェクションできない', async () => { const res = await request('api/signin', { username: 'test1', password: { @@ -82,7 +82,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('正しい情報でサインインできる', async () => { + test('正しい情報でサインインできる', async () => { const res = await request('api/signin', { username: 'test1', password: 'test1', @@ -93,7 +93,7 @@ describe('Endpoints', () => { }); describe('i/update', () => { - it('アカウント設定を更新できる', async () => { + test('アカウント設定を更新できる', async () => { const myName = '大室櫻子'; const myLocation = '七森中'; const myBirthday = '2000-09-07'; @@ -111,14 +111,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.birthday, myBirthday); }); - it('名前を空白にできない', async () => { + test('名前を空白にできない', async () => { const res = await api('/i/update', { name: ' ', }, alice); assert.strictEqual(res.status, 400); }); - it('誕生日の設定を削除できる', async () => { + test('誕生日の設定を削除できる', async () => { await api('/i/update', { birthday: '2000-09-07', }, alice); @@ -132,7 +132,7 @@ describe('Endpoints', () => { assert.strictEqual(res.body.birthday, null); }); - it('不正な誕生日の形式で怒られる', async () => { + test('不正な誕生日の形式で怒られる', async () => { const res = await api('/i/update', { birthday: '2000/09/07', }, alice); @@ -141,7 +141,7 @@ describe('Endpoints', () => { }); describe('users/show', () => { - it('ユーザーが取得できる', async () => { + test('ユーザーが取得できる', async () => { const res = await api('/users/show', { userId: alice.id, }, alice); @@ -151,14 +151,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.id, alice.id); }); - it('ユーザーが存在しなかったら怒る', async () => { + test('ユーザーが存在しなかったら怒る', async () => { const res = await api('/users/show', { userId: '000000000000000000000000', }); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/users/show', { userId: 'kyoppie', }); @@ -167,7 +167,7 @@ describe('Endpoints', () => { }); describe('notes/show', () => { - it('投稿が取得できる', async () => { + test('投稿が取得できる', async () => { const myPost = await post(alice, { text: 'test', }); @@ -182,14 +182,14 @@ describe('Endpoints', () => { assert.strictEqual(res.body.text, myPost.text); }); - it('投稿が存在しなかったら怒る', async () => { + test('投稿が存在しなかったら怒る', async () => { const res = await api('/notes/show', { noteId: '000000000000000000000000', }); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/notes/show', { noteId: 'kyoppie', }); @@ -198,7 +198,7 @@ describe('Endpoints', () => { }); describe('notes/reactions/create', () => { - it('リアクションできる', async () => { + test('リアクションできる', async () => { const bobPost = await post(bob); const alice = await signup({ username: 'alice' }); @@ -217,7 +217,7 @@ describe('Endpoints', () => { assert.strictEqual(resNote.body.reactions['🚀'], [alice.id]); }); - it('自分の投稿にもリアクションできる', async () => { + test('自分の投稿にもリアクションできる', async () => { const myPost = await post(alice); const res = await api('/notes/reactions/create', { @@ -228,7 +228,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 204); }); - it('二重にリアクションできない', async () => { + test('二重にリアクションできない', async () => { const bobPost = await post(bob); await api('/notes/reactions/create', { @@ -244,7 +244,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しない投稿にはリアクションできない', async () => { + test('存在しない投稿にはリアクションできない', async () => { const res = await api('/notes/reactions/create', { noteId: '000000000000000000000000', reaction: '🚀', @@ -253,13 +253,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/notes/reactions/create', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/notes/reactions/create', { noteId: 'kyoppie', reaction: '🚀', @@ -270,7 +270,7 @@ describe('Endpoints', () => { }); describe('following/create', () => { - it('フォローできる', async () => { + test('フォローできる', async () => { const res = await api('/following/create', { userId: alice.id, }, bob); @@ -278,7 +278,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); }); - it('既にフォローしている場合は怒る', async () => { + test('既にフォローしている場合は怒る', async () => { const res = await api('/following/create', { userId: alice.id, }, bob); @@ -286,7 +286,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーはフォローできない', async () => { + test('存在しないユーザーはフォローできない', async () => { const res = await api('/following/create', { userId: '000000000000000000000000', }, alice); @@ -294,7 +294,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('自分自身はフォローできない', async () => { + test('自分自身はフォローできない', async () => { const res = await api('/following/create', { userId: alice.id, }, alice); @@ -302,13 +302,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/following/create', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/following/create', { userId: 'foo', }, alice); @@ -318,7 +318,7 @@ describe('Endpoints', () => { }); describe('following/delete', () => { - it('フォロー解除できる', async () => { + test('フォロー解除できる', async () => { await api('/following/create', { userId: alice.id, }, bob); @@ -330,7 +330,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 200); }); - it('フォローしていない場合は怒る', async () => { + test('フォローしていない場合は怒る', async () => { const res = await api('/following/delete', { userId: alice.id, }, bob); @@ -338,7 +338,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーはフォロー解除できない', async () => { + test('存在しないユーザーはフォロー解除できない', async () => { const res = await api('/following/delete', { userId: '000000000000000000000000', }, alice); @@ -346,7 +346,7 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('自分自身はフォロー解除できない', async () => { + test('自分自身はフォロー解除できない', async () => { const res = await api('/following/delete', { userId: alice.id, }, alice); @@ -354,13 +354,13 @@ describe('Endpoints', () => { assert.strictEqual(res.status, 400); }); - it('空のパラメータで怒られる', async () => { + test('空のパラメータで怒られる', async () => { const res = await api('/following/delete', {}, alice); assert.strictEqual(res.status, 400); }); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/following/delete', { userId: 'kyoppie', }, alice); @@ -371,7 +371,7 @@ describe('Endpoints', () => { /* describe('/i', () => { - it('', async () => { + test('', async () => { }); }); */ @@ -402,7 +402,7 @@ describe('API: Endpoints', () => { }); describe('drive', () => { - it('ドライブ情報を取得できる', async () => { + test('ドライブ情報を取得できる', async () => { await uploadFile({ userId: alice.id, size: 256 @@ -423,7 +423,7 @@ describe('API: Endpoints', () => { }); describe('drive/files/create', () => { - it('ファイルを作成できる', async () => { + test('ファイルを作成できる', async () => { const res = await uploadFile(alice); assert.strictEqual(res.status, 200); @@ -431,7 +431,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, 'Lenna.png'); })); - it('ファイルに名前を付けられる', async () => { + test('ファイルに名前を付けられる', async () => { const res = await assert.request(server) .post('/drive/files/create') .field('i', alice.token) @@ -443,13 +443,13 @@ describe('API: Endpoints', () => { expect(res.body).have.property('name').eql('Belmond.png'); })); - it('ファイル無しで怒られる', async () => { + test('ファイル無しで怒られる', async () => { const res = await api('/drive/files/create', {}, alice); assert.strictEqual(res.status, 400); })); - it('SVGファイルを作成できる', async () => { + test('SVGファイルを作成できる', async () => { const res = await uploadFile(alice, __dirname + '/resources/image.svg'); assert.strictEqual(res.status, 200); @@ -460,7 +460,7 @@ describe('API: Endpoints', () => { }); describe('drive/files/update', () => { - it('名前を更新できる', async () => { + test('名前を更新できる', async () => { const file = await uploadFile(alice); const newName = 'いちごパスタ.png'; @@ -474,7 +474,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, newName); })); - it('他人のファイルは更新できない', async () => { + test('他人のファイルは更新できない', async () => { const file = await uploadFile(bob); const res = await api('/drive/files/update', { @@ -485,7 +485,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('親フォルダを更新できる', async () => { + test('親フォルダを更新できる', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { name: 'test' @@ -501,7 +501,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.folderId, folder.id); })); - it('親フォルダを無しにできる', async () => { + test('親フォルダを無しにできる', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { @@ -523,7 +523,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.folderId, null); })); - it('他人のフォルダには入れられない', async () => { + test('他人のフォルダには入れられない', async () => { const file = await uploadFile(alice); const folder = (await api('/drive/folders/create', { name: 'test' @@ -537,7 +537,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないフォルダで怒られる', async () => { + test('存在しないフォルダで怒られる', async () => { const file = await uploadFile(alice); const res = await api('/drive/files/update', { @@ -548,7 +548,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なフォルダIDで怒られる', async () => { + test('不正なフォルダIDで怒られる', async () => { const file = await uploadFile(alice); const res = await api('/drive/files/update', { @@ -559,7 +559,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('ファイルが存在しなかったら怒る', async () => { + test('ファイルが存在しなかったら怒る', async () => { const res = await api('/drive/files/update', { fileId: '000000000000000000000000', name: 'いちごパスタ.png' @@ -568,7 +568,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('間違ったIDで怒られる', async () => { + test('間違ったIDで怒られる', async () => { const res = await api('/drive/files/update', { fileId: 'kyoppie', name: 'いちごパスタ.png' @@ -579,7 +579,7 @@ describe('API: Endpoints', () => { }); describe('drive/folders/create', () => { - it('フォルダを作成できる', async () => { + test('フォルダを作成できる', async () => { const res = await api('/drive/folders/create', { name: 'test' }, alice); @@ -591,7 +591,7 @@ describe('API: Endpoints', () => { }); describe('drive/folders/update', () => { - it('名前を更新できる', async () => { + test('名前を更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -606,7 +606,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.name, 'new name'); })); - it('他人のフォルダを更新できない', async () => { + test('他人のフォルダを更新できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, bob)).body; @@ -619,7 +619,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('親フォルダを更新できる', async () => { + test('親フォルダを更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -637,7 +637,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.parentId, parentFolder.id); })); - it('親フォルダを無しに更新できる', async () => { + test('親フォルダを無しに更新できる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -659,7 +659,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.parentId, null); })); - it('他人のフォルダを親フォルダに設定できない', async () => { + test('他人のフォルダを親フォルダに設定できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -675,7 +675,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない', async () => { + test('フォルダが循環するような構造にできない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -695,7 +695,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない(再帰的)', async () => { + test('フォルダが循環するような構造にできない(再帰的)', async () => { const folderA = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -722,7 +722,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('フォルダが循環するような構造にできない(自身)', async () => { + test('フォルダが循環するような構造にできない(自身)', async () => { const folderA = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -735,7 +735,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しない親フォルダを設定できない', async () => { + test('存在しない親フォルダを設定できない', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -748,7 +748,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正な親フォルダIDで怒られる', async () => { + test('不正な親フォルダIDで怒られる', async () => { const folder = (await api('/drive/folders/create', { name: 'test' }, alice)).body; @@ -761,7 +761,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないフォルダを更新できない', async () => { + test('存在しないフォルダを更新できない', async () => { const res = await api('/drive/folders/update', { folderId: '000000000000000000000000' }, alice); @@ -769,7 +769,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なフォルダIDで怒られる', async () => { + test('不正なフォルダIDで怒られる', async () => { const res = await api('/drive/folders/update', { folderId: 'foo' }, alice); @@ -779,7 +779,7 @@ describe('API: Endpoints', () => { }); describe('messaging/messages/create', () => { - it('メッセージを送信できる', async () => { + test('メッセージを送信できる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id, text: 'test' @@ -790,7 +790,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.body.text, 'test'); })); - it('自分自身にはメッセージを送信できない', async () => { + test('自分自身にはメッセージを送信できない', async () => { const res = await api('/messaging/messages/create', { userId: alice.id, text: 'Yo' @@ -799,7 +799,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('存在しないユーザーにはメッセージを送信できない', async () => { + test('存在しないユーザーにはメッセージを送信できない', async () => { const res = await api('/messaging/messages/create', { userId: '000000000000000000000000', text: 'test' @@ -808,7 +808,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('不正なユーザーIDで怒られる', async () => { + test('不正なユーザーIDで怒られる', async () => { const res = await api('/messaging/messages/create', { userId: 'foo', text: 'test' @@ -817,7 +817,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('テキストが無くて怒られる', async () => { + test('テキストが無くて怒られる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id }, alice); @@ -825,7 +825,7 @@ describe('API: Endpoints', () => { assert.strictEqual(res.status, 400); })); - it('文字数オーバーで怒られる', async () => { + test('文字数オーバーで怒られる', async () => { const res = await api('/messaging/messages/create', { userId: bob.id, text: '!'.repeat(1001) @@ -836,7 +836,7 @@ describe('API: Endpoints', () => { }); describe('notes/replies', () => { - it('自分に閲覧権限のない投稿は含まれない', async () => { + test('自分に閲覧権限のない投稿は含まれない', async () => { const alicePost = await post(alice, { text: 'foo' }); @@ -859,7 +859,7 @@ describe('API: Endpoints', () => { }); describe('notes/timeline', () => { - it('フォロワー限定投稿が含まれる', async () => { + test('フォロワー限定投稿が含まれる', async () => { await api('/following/create', { userId: alice.id }, bob); diff --git a/packages/backend/test/_e2e/fetch-resource.ts b/packages/backend/test/_e2e/fetch-resource.ts index 344022dec7..7ae133496a 100644 --- a/packages/backend/test/_e2e/fetch-resource.ts +++ b/packages/backend/test/_e2e/fetch-resource.ts @@ -35,38 +35,38 @@ describe('Fetch resource', () => { }); describe('Common', () => { - it('meta', async () => { + test('meta', async () => { const res = await request('/meta', { }); assert.strictEqual(res.status, 200); }); - it('GET root', async () => { + test('GET root', async () => { const res = await simpleGet('/'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET docs', async () => { + test('GET docs', async () => { const res = await simpleGet('/docs/ja-JP/about'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET api-doc', async () => { + test('GET api-doc', async () => { const res = await simpleGet('/api-doc'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('GET api.json', async () => { + test('GET api.json', async () => { const res = await simpleGet('/api.json'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, JSON); }); - it('Validate api.json', async () => { + test('Validate api.json', async () => { const config = await openapi.loadConfig(); const result = await openapi.bundle({ config, @@ -80,25 +80,25 @@ describe('Fetch resource', () => { assert.strictEqual(result.problems.length, 0); }); - it('GET favicon.ico', async () => { + test('GET favicon.ico', async () => { const res = await simpleGet('/favicon.ico'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/x-icon'); }); - it('GET apple-touch-icon.png', async () => { + test('GET apple-touch-icon.png', async () => { const res = await simpleGet('/apple-touch-icon.png'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/png'); }); - it('GET twemoji svg', async () => { + test('GET twemoji svg', async () => { const res = await simpleGet('/twemoji/2764.svg'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/svg+xml'); }); - it('GET twemoji svg with hyphen', async () => { + test('GET twemoji svg with hyphen', async () => { const res = await simpleGet('/twemoji/2764-fe0f-200d-1f525.svg'); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'image/svg+xml'); @@ -106,25 +106,25 @@ describe('Fetch resource', () => { }); describe('/@:username', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/@${alice.username}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/@${alice.username}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => HTML', async () => { + test('Prefer HTML => HTML', async () => { const res = await simpleGet(`/@${alice.username}`, PREFER_HTML); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('Unspecified => HTML', async () => { + test('Unspecified => HTML', async () => { const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); @@ -132,25 +132,25 @@ describe('Fetch resource', () => { }); describe('/users/:id', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/users/${alice.id}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/users/${alice.id}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => Redirect to /@:username', async () => { + test('Prefer HTML => Redirect to /@:username', async () => { const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML); assert.strictEqual(res.status, 302); assert.strictEqual(res.location, `/@${alice.username}`); }); - it('Undecided => HTML', async () => { + test('Undecided => HTML', async () => { const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED); assert.strictEqual(res.status, 302); assert.strictEqual(res.location, `/@${alice.username}`); @@ -158,25 +158,25 @@ describe('Fetch resource', () => { }); describe('/notes/:id', () => { - it('Only AP => AP', async () => { + test('Only AP => AP', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer AP => AP', async () => { + test('Prefer AP => AP', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, AP); }); - it('Prefer HTML => HTML', async () => { + test('Prefer HTML => HTML', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); }); - it('Unspecified => HTML', async () => { + test('Unspecified => HTML', async () => { const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, HTML); @@ -184,19 +184,19 @@ describe('Fetch resource', () => { }); describe('Feeds', () => { - it('RSS', async () => { + test('RSS', async () => { const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8'); }); - it('ATOM', async () => { + test('ATOM', async () => { const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8'); }); - it('JSON', async () => { + test('JSON', async () => { const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED); assert.strictEqual(res.status, 200); assert.strictEqual(res.type, 'application/json; charset=utf-8'); diff --git a/packages/backend/test/_e2e/ff-visibility.ts b/packages/backend/test/_e2e/ff-visibility.ts index 38be0eba24..84a5b5ef28 100644 --- a/packages/backend/test/_e2e/ff-visibility.ts +++ b/packages/backend/test/_e2e/ff-visibility.ts @@ -22,7 +22,7 @@ describe('FF visibility', () => { await shutdownServer(p); }); - it('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { + test('ffVisibility が public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { await request('/i/update', { ffVisibility: 'public', }, alice); @@ -40,7 +40,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーを自分で見れる', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -58,7 +58,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーを非フォロワーが見れない', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -74,7 +74,7 @@ describe('FF visibility', () => { assert.strictEqual(followersRes.status, 400); }); - it('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => { + test('ffVisibility が followers なユーザーのフォロー/フォロワーをフォロワーが見れる', async () => { await request('/i/update', { ffVisibility: 'followers', }, alice); @@ -96,7 +96,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => { + test('ffVisibility が private なユーザーのフォロー/フォロワーを自分で見れる', async () => { await request('/i/update', { ffVisibility: 'private', }, alice); @@ -114,7 +114,7 @@ describe('FF visibility', () => { assert.strictEqual(Array.isArray(followersRes.body), true); }); - it('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => { + test('ffVisibility が private なユーザーのフォロー/フォロワーを他人が見れない', async () => { await request('/i/update', { ffVisibility: 'private', }, alice); @@ -131,7 +131,7 @@ describe('FF visibility', () => { }); describe('AP', () => { - it('ffVisibility が public 以外ならばAPからは取得できない', async () => { + test('ffVisibility が public 以外ならばAPからは取得できない', async () => { { await request('/i/update', { ffVisibility: 'public', diff --git a/packages/backend/test/_e2e/mute.ts b/packages/backend/test/_e2e/mute.ts index 2313773678..8f7f72bb97 100644 --- a/packages/backend/test/_e2e/mute.ts +++ b/packages/backend/test/_e2e/mute.ts @@ -23,7 +23,7 @@ describe('Mute', () => { await shutdownServer(p); }); - it('ミュート作成', async () => { + test('ミュート作成', async () => { const res = await request('/mute/create', { userId: carol.id, }, alice); @@ -31,7 +31,7 @@ describe('Mute', () => { assert.strictEqual(res.status, 204); }); - it('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => { + test('「自分宛ての投稿」にミュートしているユーザーの投稿が含まれない', async () => { const bobNote = await post(bob, { text: '@alice hi' }); const carolNote = await post(carol, { text: '@alice hi' }); @@ -43,7 +43,7 @@ describe('Mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); - it('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { + test('ミュートしているユーザーからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -55,7 +55,7 @@ describe('Mute', () => { assert.strictEqual(res.body.hasUnreadMentions, false); }); - it('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { + test('ミュートしているユーザーからメンションされても、ストリームに unreadMention イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -64,7 +64,7 @@ describe('Mute', () => { assert.strictEqual(fired, false); }); - it('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { + test('ミュートしているユーザーからメンションされても、ストリームに unreadNotification イベントが流れてこない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); await request('/notifications/mark-all-as-read', {}, alice); @@ -75,7 +75,7 @@ describe('Mute', () => { }); describe('Timeline', () => { - it('タイムラインにミュートしているユーザーの投稿が含まれない', async () => { + test('タイムラインにミュートしているユーザーの投稿が含まれない', async () => { const aliceNote = await post(alice); const bobNote = await post(bob); const carolNote = await post(carol); @@ -89,7 +89,7 @@ describe('Mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolNote.id), false); }); - it('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => { + test('タイムラインにミュートしているユーザーの投稿のRenoteが含まれない', async () => { const aliceNote = await post(alice); const carolNote = await post(carol); const bobNote = await post(bob, { @@ -107,7 +107,7 @@ describe('Mute', () => { }); describe('Notification', () => { - it('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => { + test('通知にミュートしているユーザーの通知が含まれない(リアクション)', async () => { const aliceNote = await post(alice); await react(bob, aliceNote, 'like'); await react(carol, aliceNote, 'like'); diff --git a/packages/backend/test/_e2e/note.ts b/packages/backend/test/_e2e/note.ts index d75a5c8285..47af6808f6 100644 --- a/packages/backend/test/_e2e/note.ts +++ b/packages/backend/test/_e2e/note.ts @@ -24,7 +24,7 @@ describe('Note', () => { await shutdownServer(p); }); - it('投稿できる', async () => { + test('投稿できる', async () => { const post = { text: 'test', }; @@ -36,7 +36,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.text, post.text); }); - it('ファイルを添付できる', async () => { + test('ファイルを添付できる', async () => { const file = await uploadUrl(alice, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { @@ -48,7 +48,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, [file.id]); }, 1000 * 10); - it('他人のファイルは無視', async () => { + test('他人のファイルは無視', async () => { const file = await uploadUrl(bob, 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg'); const res = await request('/notes/create', { @@ -61,7 +61,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }, 1000 * 10); - it('存在しないファイルは無視', async () => { + test('存在しないファイルは無視', async () => { const res = await request('/notes/create', { text: 'test', fileIds: ['000000000000000000000000'], @@ -72,7 +72,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }); - it('不正なファイルIDは無視', async () => { + test('不正なファイルIDは無視', async () => { const res = await request('/notes/create', { fileIds: ['kyoppie'], }, alice); @@ -81,7 +81,7 @@ describe('Note', () => { assert.deepStrictEqual(res.body.createdNote.fileIds, []); }); - it('返信できる', async () => { + test('返信できる', async () => { const bobPost = await post(bob, { text: 'foo', }); @@ -100,7 +100,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.reply.text, bobPost.text); }); - it('renoteできる', async () => { + test('renoteできる', async () => { const bobPost = await post(bob, { text: 'test', }); @@ -117,7 +117,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); - it('引用renoteできる', async () => { + test('引用renoteできる', async () => { const bobPost = await post(bob, { text: 'test', }); @@ -136,7 +136,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); - it('文字数ぎりぎりで怒られない', async () => { + test('文字数ぎりぎりで怒られない', async () => { const post = { text: '!'.repeat(3000), }; @@ -144,7 +144,7 @@ describe('Note', () => { assert.strictEqual(res.status, 200); }); - it('文字数オーバーで怒られる', async () => { + test('文字数オーバーで怒られる', async () => { const post = { text: '!'.repeat(3001), }; @@ -152,7 +152,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないリプライ先で怒られる', async () => { + test('存在しないリプライ先で怒られる', async () => { const post = { text: 'test', replyId: '000000000000000000000000', @@ -161,7 +161,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないrenote対象で怒られる', async () => { + test('存在しないrenote対象で怒られる', async () => { const post = { renoteId: '000000000000000000000000', }; @@ -169,7 +169,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('不正なリプライ先IDで怒られる', async () => { + test('不正なリプライ先IDで怒られる', async () => { const post = { text: 'test', replyId: 'foo', @@ -178,7 +178,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('不正なrenote対象IDで怒られる', async () => { + test('不正なrenote対象IDで怒られる', async () => { const post = { renoteId: 'foo', }; @@ -186,7 +186,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('存在しないユーザーにメンションできる', async () => { + test('存在しないユーザーにメンションできる', async () => { const post = { text: '@ghost yo', }; @@ -198,7 +198,7 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.text, post.text); }); - it('同じユーザーに複数メンションしても内部的にまとめられる', async () => { + test('同じユーザーに複数メンションしても内部的にまとめられる', async () => { const post = { text: '@bob @bob @bob yo', }; @@ -214,7 +214,7 @@ describe('Note', () => { }); describe('notes/create', () => { - it('投票を添付できる', async () => { + test('投票を添付できる', async () => { const res = await request('/notes/create', { text: 'test', poll: { @@ -227,14 +227,14 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.poll != null, true); }); - it('投票の選択肢が無くて怒られる', async () => { + test('投票の選択肢が無くて怒られる', async () => { const res = await request('/notes/create', { poll: {}, }, alice); assert.strictEqual(res.status, 400); }); - it('投票の選択肢が無くて怒られる (空の配列)', async () => { + test('投票の選択肢が無くて怒られる (空の配列)', async () => { const res = await request('/notes/create', { poll: { choices: [], @@ -243,7 +243,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('投票の選択肢が1つで怒られる', async () => { + test('投票の選択肢が1つで怒られる', async () => { const res = await request('/notes/create', { poll: { choices: ['Strawberry Pasta'], @@ -252,7 +252,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('投票できる', async () => { + test('投票できる', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -268,7 +268,7 @@ describe('Note', () => { assert.strictEqual(res.status, 204); }); - it('複数投票できない', async () => { + test('複数投票できない', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -289,7 +289,7 @@ describe('Note', () => { assert.strictEqual(res.status, 400); }); - it('許可されている場合は複数投票できる', async () => { + test('許可されている場合は複数投票できる', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -316,7 +316,7 @@ describe('Note', () => { assert.strictEqual(res.status, 204); }); - it('締め切られている場合は投票できない', async () => { + test('締め切られている場合は投票できない', async () => { const { body } = await request('/notes/create', { text: 'test', poll: { @@ -337,7 +337,7 @@ describe('Note', () => { }); describe('notes/delete', () => { - it('delete a reply', async () => { + test('delete a reply', async () => { const mainNoteRes = await api('notes/create', { text: 'main post', }, alice); diff --git a/packages/backend/test/_e2e/streaming.ts b/packages/backend/test/_e2e/streaming.ts index 4dad322e99..790451d9b4 100644 --- a/packages/backend/test/_e2e/streaming.ts +++ b/packages/backend/test/_e2e/streaming.ts @@ -78,7 +78,7 @@ describe('Streaming', () => { }); describe('Events', () => { - it('mention event', async () => { + test('mention event', async () => { const fired = await waitFire( kyoko, 'main', // kyoko:main () => post(ayano, { text: 'foo @kyoko bar' }), // ayano mention => kyoko @@ -88,7 +88,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('renote event', async () => { + test('renote event', async () => { const fired = await waitFire( kyoko, 'main', // kyoko:main () => post(ayano, { renoteId: kyokoNote.id }), // ayano renote @@ -100,7 +100,7 @@ describe('Streaming', () => { }); describe('Home Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:Home () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -110,7 +110,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーの投稿が流れる', async () => { + test('フォローしているユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo' }, kyoko), // kyoko posts @@ -120,7 +120,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないユーザーの投稿は流れない', async () => { + test('フォローしていないユーザーの投稿は流れない', async () => { const fired = await waitFire( kyoko, 'homeTimeline', // kyoko:home () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -130,7 +130,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているユーザーのダイレクト投稿が流れる', async () => { + test('フォローしているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko dm => ayano @@ -140,7 +140,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => { + test('フォローしているユーザーでも自分が指定されていないダイレクト投稿は流れない', async () => { const fired = await waitFire( ayano, 'homeTimeline', // ayano:home () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, kyoko), // kyoko dm => chitose @@ -152,7 +152,7 @@ describe('Streaming', () => { }); // Home describe('Local Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -162,7 +162,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -172,7 +172,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('リモートユーザーの投稿は流れない', async () => { + test('リモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -182,7 +182,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => { + test('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo' }, akari), // akari posts @@ -192,7 +192,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('ホーム指定の投稿は流れない', async () => { + test('ホーム指定の投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko home posts @@ -202,7 +202,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => { + test('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko DM => ayano @@ -212,7 +212,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { + test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( ayano, 'localTimeline', // ayano:Local () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose), @@ -224,7 +224,7 @@ describe('Streaming', () => { }); describe('Hybrid Timeline', () => { - it('自分の投稿が流れる', async () => { + test('自分の投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, ayano), // ayano posts @@ -234,7 +234,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -244,7 +244,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているリモートユーザーの投稿が流れる', async () => { + test('フォローしているリモートユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, akari), // akari posts @@ -254,7 +254,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないリモートユーザーの投稿は流れない', async () => { + test('フォローしていないリモートユーザーの投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -264,7 +264,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしているユーザーのダイレクト投稿が流れる', async () => { + test('フォローしているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), @@ -274,7 +274,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしているユーザーのホーム投稿が流れる', async () => { + test('フォローしているユーザーのホーム投稿が流れる', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), @@ -284,7 +284,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないローカルユーザーのホーム投稿は流れない', async () => { + test('フォローしていないローカルユーザーのホーム投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'home' }, chitose), @@ -294,7 +294,7 @@ describe('Streaming', () => { assert.strictEqual(fired, false); }); - it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { + test('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( ayano, 'hybridTimeline', // ayano:Hybrid () => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose), @@ -306,7 +306,7 @@ describe('Streaming', () => { }); describe('Global Timeline', () => { - it('フォローしていないローカルユーザーの投稿が流れる', async () => { + test('フォローしていないローカルユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo' }, chitose), // chitose posts @@ -316,7 +316,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('フォローしていないリモートユーザーの投稿が流れる', async () => { + test('フォローしていないリモートユーザーの投稿が流れる', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts @@ -326,7 +326,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('ホーム投稿は流れない', async () => { + test('ホーム投稿は流れない', async () => { const fired = await waitFire( ayano, 'globalTimeline', // ayano:Global () => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko posts @@ -338,7 +338,7 @@ describe('Streaming', () => { }); describe('UserList Timeline', () => { - it('リストに入れているユーザーの投稿が流れる', async () => { + test('リストに入れているユーザーの投稿が流れる', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo' }, ayano), @@ -349,7 +349,7 @@ describe('Streaming', () => { assert.strictEqual(fired, true); }); - it('リストに入れていないユーザーの投稿は流れない', async () => { + test('リストに入れていないユーザーの投稿は流れない', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo' }, chinatsu), @@ -361,7 +361,7 @@ describe('Streaming', () => { }); // #4471 - it('リストに入れているユーザーのダイレクト投稿が流れる', async () => { + test('リストに入れているユーザーのダイレクト投稿が流れる', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [chitose.id] }, ayano), @@ -373,7 +373,7 @@ describe('Streaming', () => { }); // #4335 - it('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => { + test('リストに入れているがフォローはしてないユーザーのフォロワー宛て投稿は流れない', async () => { const fired = await waitFire( chitose, 'userList', () => api('notes/create', { text: 'foo', visibility: 'followers' }, kyoko), @@ -386,7 +386,7 @@ describe('Streaming', () => { }); describe('Hashtag Timeline', () => { - it('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる', () => new Promise(async done => { const ws = await connectStream(chitose, 'hashtag', ({ type, body }) => { if (type === 'note') { assert.deepStrictEqual(body.text, '#foo'); @@ -404,7 +404,7 @@ describe('Streaming', () => { }); })); - it('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (AND)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; @@ -442,7 +442,7 @@ describe('Streaming', () => { }, 3000); })); - it('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (OR)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; @@ -488,7 +488,7 @@ describe('Streaming', () => { }, 3000); })); - it('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { + test('指定したハッシュタグの投稿が流れる (AND + OR)', () => new Promise(async done => { let fooCount = 0; let barCount = 0; let fooBarCount = 0; diff --git a/packages/backend/test/_e2e/thread-mute.ts b/packages/backend/test/_e2e/thread-mute.ts index 0ed9aa0666..890b52a8c1 100644 --- a/packages/backend/test/_e2e/thread-mute.ts +++ b/packages/backend/test/_e2e/thread-mute.ts @@ -22,7 +22,7 @@ describe('Note thread mute', () => { await shutdownServer(p); }); - it('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => { + test('notes/mentions にミュートしているスレッドの投稿が含まれない', async () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); @@ -40,7 +40,7 @@ describe('Note thread mute', () => { assert.strictEqual(res.body.some((note: any) => note.id === carolReplyWithoutMention.id), false); }); - it('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { + test('ミュートしているスレッドからメンションされても、hasUnreadMentions が true にならない', async () => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -56,7 +56,7 @@ describe('Note thread mute', () => { assert.strictEqual(res.body.hasUnreadMentions, false); }); - it('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => { + test('ミュートしているスレッドからメンションされても、ストリームに unreadMention イベントが流れてこない', () => new Promise(async done => { // 状態リセット await request('/i/read-all-unread-notes', {}, alice); @@ -82,7 +82,7 @@ describe('Note thread mute', () => { }, 5000); })); - it('i/notifications にミュートしているスレッドの通知が含まれない', async () => { + test('i/notifications にミュートしているスレッドの通知が含まれない', async () => { const bobNote = await post(bob, { text: '@alice @carol root note' }); const aliceReply = await post(alice, { replyId: bobNote.id, text: '@bob @carol child note' }); diff --git a/packages/backend/test/_e2e/user-notes.ts b/packages/backend/test/_e2e/user-notes.ts index 353875634c..a6cc1057f9 100644 --- a/packages/backend/test/_e2e/user-notes.ts +++ b/packages/backend/test/_e2e/user-notes.ts @@ -32,7 +32,7 @@ describe('users/notes', () => { await shutdownServer(p); }); - it('ファイルタイプ指定 (jpg)', async () => { + test('ファイルタイプ指定 (jpg)', async () => { const res = await request('/users/notes', { userId: alice.id, fileType: ['image/jpeg'], @@ -45,7 +45,7 @@ describe('users/notes', () => { assert.strictEqual(res.body.some((note: any) => note.id === jpgPngNote.id), true); }); - it('ファイルタイプ指定 (jpg or png)', async () => { + test('ファイルタイプ指定 (jpg or png)', async () => { const res = await request('/users/notes', { userId: alice.id, fileType: ['image/jpeg', 'image/png'], diff --git a/packages/backend/test/prelude/maybe.ts b/packages/backend/test/prelude/maybe.ts index c1ff63eada..b8679c1071 100644 --- a/packages/backend/test/prelude/maybe.ts +++ b/packages/backend/test/prelude/maybe.ts @@ -2,17 +2,17 @@ import * as assert from 'assert'; import { just, nothing } from '../../src/misc/prelude/maybe.js'; describe('just', () => { - it('has a value', () => { + test('has a value', () => { assert.deepStrictEqual(just(3).isJust(), true); }); - it('has the inverse called get', () => { + test('has the inverse called get', () => { assert.deepStrictEqual(just(3).get(), 3); }); }); describe('nothing', () => { - it('has no value', () => { + test('has no value', () => { assert.deepStrictEqual(nothing().isJust(), false); }); }); diff --git a/packages/backend/test/prelude/url.ts b/packages/backend/test/prelude/url.ts index 574f2fffdb..23b6b22bb0 100644 --- a/packages/backend/test/prelude/url.ts +++ b/packages/backend/test/prelude/url.ts @@ -2,7 +2,7 @@ import * as assert from 'assert'; import { query } from '../../src/misc/prelude/url.js'; describe('url', () => { - it('query', () => { + test('query', () => { const s = query({ foo: 'ふぅ', bar: 'b a r', diff --git a/packages/backend/test/tests/activitypub.ts b/packages/backend/test/tests/activitypub.ts index 08ec0a59ea..19fb5d90d7 100644 --- a/packages/backend/test/tests/activitypub.ts +++ b/packages/backend/test/tests/activitypub.ts @@ -27,7 +27,7 @@ describe('ActivityPub', () => { content: 'あ', }; - it('Minimum Actor', async () => { + test('Minimum Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createPerson } = await import('../../src/activitypub/models/person.js'); @@ -41,7 +41,7 @@ describe('ActivityPub', () => { assert.deepStrictEqual(user.inbox, actor.inbox); }); - it('Minimum Note', async () => { + test('Minimum Note', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createNote } = await import('../../src/activitypub/models/note.js'); @@ -74,7 +74,7 @@ describe('ActivityPub', () => { outbox: `${actorId}/outbox`, }; - it('Actor', async () => { + test('Actor', async () => { const { MockResolver } = await import('../misc/mock-resolver.js'); const { createPerson } = await import('../../src/activitypub/models/person.js'); diff --git a/packages/backend/test/tests/ap-request.ts b/packages/backend/test/tests/ap-request.ts index d628f03f44..8c586861ad 100644 --- a/packages/backend/test/tests/ap-request.ts +++ b/packages/backend/test/tests/ap-request.ts @@ -19,7 +19,7 @@ export const buildParsedSignature = (signingString: string, signature: string, a }; describe('ap-request', () => { - it('createSignedPost with verify', async () => { + test('createSignedPost with verify', async () => { const keypair = await genRsaKeyPair(); const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; const url = 'https://example.com/inbox'; @@ -37,7 +37,7 @@ describe('ap-request', () => { assert.deepStrictEqual(result, true); }); - it('createSignedGet with verify', async () => { + test('createSignedGet with verify', async () => { const keypair = await genRsaKeyPair(); const key = { keyId: 'x', 'privateKeyPem': keypair.privateKey }; const url = 'https://example.com/outbox'; diff --git a/packages/backend/test/tests/extract-mentions.ts b/packages/backend/test/tests/extract-mentions.ts index 4f9cb68763..e81d04c2db 100644 --- a/packages/backend/test/tests/extract-mentions.ts +++ b/packages/backend/test/tests/extract-mentions.ts @@ -4,7 +4,7 @@ import { parse } from 'mfm-js'; import { extractMentions } from '../../src/misc/extract-mentions.js'; describe('Extract mentions', () => { - it('simple', () => { + test('simple', () => { const ast = parse('@foo @bar @baz')!; const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ @@ -22,7 +22,7 @@ describe('Extract mentions', () => { }]); }); - it('nested', () => { + test('nested', () => { const ast = parse('@foo **@bar** @baz')!; const mentions = extractMentions(ast); assert.deepStrictEqual(mentions, [{ diff --git a/packages/backend/test/tests/mfm.ts b/packages/backend/test/tests/mfm.ts index 5087e84a1a..884f39d7fb 100644 --- a/packages/backend/test/tests/mfm.ts +++ b/packages/backend/test/tests/mfm.ts @@ -5,13 +5,13 @@ import { toHtml } from '../../src/mfm/to-html.js'; import { fromHtml } from '../../src/mfm/from-html.js'; describe('toHtml', () => { - it('br', () => { + test('br', () => { const input = 'foo\nbar\nbaz'; const output = '

foo
bar
baz

'; assert.equal(toHtml(mfm.parse(input)), output); }); - it('br alt', () => { + test('br alt', () => { const input = 'foo\r\nbar\rbaz'; const output = '

foo
bar
baz

'; assert.equal(toHtml(mfm.parse(input)), output); @@ -19,71 +19,71 @@ describe('toHtml', () => { }); describe('fromHtml', () => { - it('p', () => { + test('p', () => { assert.deepStrictEqual(fromHtml('

a

b

'), 'a\n\nb'); }); - it('block element', () => { + test('block element', () => { assert.deepStrictEqual(fromHtml('
a
b
'), 'a\nb'); }); - it('inline element', () => { + test('inline element', () => { assert.deepStrictEqual(fromHtml('
  • a
  • b
'), 'a\nb'); }); - it('block code', () => { + test('block code', () => { assert.deepStrictEqual(fromHtml('
a\nb
'), '```\na\nb\n```'); }); - it('inline code', () => { + test('inline code', () => { assert.deepStrictEqual(fromHtml('a'), '`a`'); }); - it('quote', () => { + test('quote', () => { assert.deepStrictEqual(fromHtml('
a\nb
'), '> a\n> b'); }); - it('br', () => { + test('br', () => { assert.deepStrictEqual(fromHtml('

abc

d

'), 'abc\n\nd'); }); - it('link with different text', () => { + test('link with different text', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c](https://example.com/b) d'); }); - it('link with different text, but not encoded', () => { + test('link with different text, but not encoded', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c]() d'); }); - it('link with same text', () => { + test('link with same text', () => { assert.deepStrictEqual(fromHtml('

a https://example.com/b d

'), 'a https://example.com/b d'); }); - it('link with same text, but not encoded', () => { + test('link with same text, but not encoded', () => { assert.deepStrictEqual(fromHtml('

a https://example.com/ä d

'), 'a d'); }); - it('link with no url', () => { + test('link with no url', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a [c](b) d'); }); - it('link without href', () => { + test('link without href', () => { assert.deepStrictEqual(fromHtml('

a c d

'), 'a c d'); }); - it('link without text', () => { + test('link without text', () => { assert.deepStrictEqual(fromHtml('

a d

'), 'a https://example.com/b d'); }); - it('link without both', () => { + test('link without both', () => { assert.deepStrictEqual(fromHtml('

a d

'), 'a d'); }); - it('mention', () => { + test('mention', () => { assert.deepStrictEqual(fromHtml('

a @user d

'), 'a @user@example.com d'); }); - it('hashtag', () => { + test('hashtag', () => { assert.deepStrictEqual(fromHtml('

a #a d

', ['#a']), 'a #a d'); }); }); diff --git a/packages/backend/test/tests/reaction-lib.ts b/packages/backend/test/tests/reaction-lib.ts index 7c61dc76c2..2e767f7697 100644 --- a/packages/backend/test/tests/reaction-lib.ts +++ b/packages/backend/test/tests/reaction-lib.ts @@ -4,79 +4,79 @@ import * as assert from 'assert'; import { toDbReaction } from '../src/misc/reaction-lib.js'; describe('toDbReaction', async () => { - it('既存の文字列リアクションはそのまま', async () => { + test('既存の文字列リアクションはそのまま', async () => { assert.strictEqual(await toDbReaction('like'), 'like'); }); - it('Unicodeプリンは寿司化不能とするため文字列化しない', async () => { + test('Unicodeプリンは寿司化不能とするため文字列化しない', async () => { assert.strictEqual(await toDbReaction('🍮'), '🍮'); }); - it('プリン以外の既存のリアクションは文字列化する like', async () => { + test('プリン以外の既存のリアクションは文字列化する like', async () => { assert.strictEqual(await toDbReaction('👍'), 'like'); }); - it('プリン以外の既存のリアクションは文字列化する love', async () => { + test('プリン以外の既存のリアクションは文字列化する love', async () => { assert.strictEqual(await toDbReaction('❤️'), 'love'); }); - it('プリン以外の既存のリアクションは文字列化する love 異体字セレクタなし', async () => { + test('プリン以外の既存のリアクションは文字列化する love 異体字セレクタなし', async () => { assert.strictEqual(await toDbReaction('❤'), 'love'); }); - it('プリン以外の既存のリアクションは文字列化する laugh', async () => { + test('プリン以外の既存のリアクションは文字列化する laugh', async () => { assert.strictEqual(await toDbReaction('😆'), 'laugh'); }); - it('プリン以外の既存のリアクションは文字列化する hmm', async () => { + test('プリン以外の既存のリアクションは文字列化する hmm', async () => { assert.strictEqual(await toDbReaction('🤔'), 'hmm'); }); - it('プリン以外の既存のリアクションは文字列化する surprise', async () => { + test('プリン以外の既存のリアクションは文字列化する surprise', async () => { assert.strictEqual(await toDbReaction('😮'), 'surprise'); }); - it('プリン以外の既存のリアクションは文字列化する congrats', async () => { + test('プリン以外の既存のリアクションは文字列化する congrats', async () => { assert.strictEqual(await toDbReaction('🎉'), 'congrats'); }); - it('プリン以外の既存のリアクションは文字列化する angry', async () => { + test('プリン以外の既存のリアクションは文字列化する angry', async () => { assert.strictEqual(await toDbReaction('💢'), 'angry'); }); - it('プリン以外の既存のリアクションは文字列化する confused', async () => { + test('プリン以外の既存のリアクションは文字列化する confused', async () => { assert.strictEqual(await toDbReaction('😥'), 'confused'); }); - it('プリン以外の既存のリアクションは文字列化する rip', async () => { + test('プリン以外の既存のリアクションは文字列化する rip', async () => { assert.strictEqual(await toDbReaction('😇'), 'rip'); }); - it('それ以外はUnicodeのまま', async () => { + test('それ以外はUnicodeのまま', async () => { assert.strictEqual(await toDbReaction('🍅'), '🍅'); }); - it('異体字セレクタ除去', async () => { + test('異体字セレクタ除去', async () => { assert.strictEqual(await toDbReaction('㊗️'), '㊗'); }); - it('異体字セレクタ除去 必要なし', async () => { + test('異体字セレクタ除去 必要なし', async () => { assert.strictEqual(await toDbReaction('㊗'), '㊗'); }); - it('fallback - undefined', async () => { + test('fallback - undefined', async () => { assert.strictEqual(await toDbReaction(undefined), 'like'); }); - it('fallback - null', async () => { + test('fallback - null', async () => { assert.strictEqual(await toDbReaction(null), 'like'); }); - it('fallback - empty', async () => { + test('fallback - empty', async () => { assert.strictEqual(await toDbReaction(''), 'like'); }); - it('fallback - unknown', async () => { + test('fallback - unknown', async () => { assert.strictEqual(await toDbReaction('unknown'), 'like'); }); }); diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts index b876deb545..d05833560d 100644 --- a/packages/backend/test/unit/FileInfoService.ts +++ b/packages/backend/test/unit/FileInfoService.ts @@ -54,7 +54,7 @@ describe('FileInfoService', () => { await app.close(); }); - it('Empty file', async () => { + test('Empty file', async () => { const path = `${resources}/emptyfile`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -74,7 +74,7 @@ describe('FileInfoService', () => { }); }); - it('Generic JPEG', async () => { + test('Generic JPEG', async () => { const path = `${resources}/Lenna.jpg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -94,7 +94,7 @@ describe('FileInfoService', () => { }); }); - it('Generic APNG', async () => { + test('Generic APNG', async () => { const path = `${resources}/anime.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -114,7 +114,7 @@ describe('FileInfoService', () => { }); }); - it('Generic AGIF', async () => { + test('Generic AGIF', async () => { const path = `${resources}/anime.gif`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -134,7 +134,7 @@ describe('FileInfoService', () => { }); }); - it('PNG with alpha', async () => { + test('PNG with alpha', async () => { const path = `${resources}/with-alpha.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -154,7 +154,7 @@ describe('FileInfoService', () => { }); }); - it('Generic SVG', async () => { + test('Generic SVG', async () => { const path = `${resources}/image.svg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -174,7 +174,7 @@ describe('FileInfoService', () => { }); }); - it('SVG with XML definition', async () => { + test('SVG with XML definition', async () => { // https://github.com/misskey-dev/misskey/issues/4413 const path = `${resources}/with-xml-def.svg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; @@ -195,7 +195,7 @@ describe('FileInfoService', () => { }); }); - it('Dimension limit', async () => { + test('Dimension limit', async () => { const path = `${resources}/25000x25000.png`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; @@ -215,7 +215,7 @@ describe('FileInfoService', () => { }); }); - it('Rotate JPEG', async () => { + test('Rotate JPEG', async () => { const path = `${resources}/rotate.jpg`; const info = await fileInfoService.getFileInfo(path, { skipSensitiveDetection: true }) as any; delete info.warnings; diff --git a/packages/backend/test/unit/MetaService.ts b/packages/backend/test/unit/MetaService.ts index 26649d92a4..9efd8bbe70 100644 --- a/packages/backend/test/unit/MetaService.ts +++ b/packages/backend/test/unit/MetaService.ts @@ -35,7 +35,7 @@ describe('MetaService', () => { await app.close(); }); - it('fetch (cache)', async () => { + test('fetch (cache)', async () => { const db = app.get(DI.db); const spy = jest.spyOn(db, 'transaction'); @@ -45,7 +45,7 @@ describe('MetaService', () => { expect(spy).toHaveBeenCalledTimes(0); }); - it('fetch (force)', async () => { + test('fetch (force)', async () => { const db = app.get(DI.db); const spy = jest.spyOn(db, 'transaction'); diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts index 5f87fea7aa..529e923b2c 100644 --- a/packages/backend/test/unit/RelayService.ts +++ b/packages/backend/test/unit/RelayService.ts @@ -57,7 +57,7 @@ describe('RelayService', () => { await app.close(); }); - it('addRelay', async () => { + test('addRelay', async () => { const result = await relayService.addRelay('https://example.com'); expect(result.inbox).toBe('https://example.com'); @@ -68,7 +68,7 @@ describe('RelayService', () => { //expect(queueService.deliver.mock.lastCall![0].username).toBe('relay.actor'); }); - it('listRelay', async () => { + test('listRelay', async () => { const result = await relayService.listRelay(); expect(result.length).toBe(1); @@ -76,7 +76,7 @@ describe('RelayService', () => { expect(result[0].status).toBe('requesting'); }); - it('removeRelay: succ', async () => { + test('removeRelay: succ', async () => { await relayService.removeRelay('https://example.com'); expect(queueService.deliver).toHaveBeenCalled(); @@ -89,7 +89,7 @@ describe('RelayService', () => { expect(list.length).toBe(0); }); - it('removeRelay: fail', async () => { + test('removeRelay: fail', async () => { await expect(relayService.removeRelay('https://x.example.com')) .rejects.toThrow('relay not found'); }); diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts new file mode 100644 index 0000000000..221f743d3a --- /dev/null +++ b/packages/backend/test/unit/RoleService.ts @@ -0,0 +1,232 @@ +process.env.NODE_ENV = 'test'; + +import { jest } from '@jest/globals'; +import { ModuleMocker } from 'jest-mock'; +import { Test } from '@nestjs/testing'; +import { DataSource } from 'typeorm'; +import rndstr from 'rndstr'; +import { GlobalModule } from '@/GlobalModule.js'; +import { RoleService } from '@/core/RoleService.js'; +import type { Role, RolesRepository, RoleAssignmentsRepository, UsersRepository, User } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { CoreModule } from '@/core/CoreModule.js'; +import { MetaService } from '@/core/MetaService.js'; +import { genAid } from '@/misc/id/aid.js'; +import { UserCacheService } from '@/core/UserCacheService.js'; +import type { TestingModule } from '@nestjs/testing'; +import type { MockFunctionMetadata } from 'jest-mock'; + +const moduleMocker = new ModuleMocker(global); + +describe('RoleService', () => { + let app: TestingModule; + let roleService: RoleService; + let usersRepository: UsersRepository; + let rolesRepository: RolesRepository; + let roleAssignmentsRepository: RoleAssignmentsRepository; + let metaService: jest.Mocked; + + function createUser(data: Partial = {}) { + const un = rndstr('a-z0-9', 16); + return usersRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + username: un, + usernameLower: un, + ...data, + }) + .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); + } + + function createRole(data: Partial = {}) { + return rolesRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + updatedAt: new Date(), + lastUsedAt: new Date(), + description: '', + ...data, + }) + .then(x => rolesRepository.findOneByOrFail(x.identifiers[0])); + } + + async function assign(roleId: Role['id'], userId: User['id']) { + await roleAssignmentsRepository.insert({ + id: genAid(new Date()), + createdAt: new Date(), + roleId, + userId, + }); + } + + beforeEach(async () => { + app = await Test.createTestingModule({ + imports: [ + GlobalModule, + ], + providers: [ + RoleService, + UserCacheService, + ], + }) + .useMocker((token) => { + if (token === MetaService) { + return { fetch: jest.fn() }; + } + if (typeof token === 'function') { + const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata; + const Mock = moduleMocker.generateFromMetadata(mockMetadata); + return new Mock(); + } + }) + .compile(); + + app.enableShutdownHooks(); + + roleService = app.get(RoleService); + usersRepository = app.get(DI.usersRepository); + rolesRepository = app.get(DI.rolesRepository); + roleAssignmentsRepository = app.get(DI.roleAssignmentsRepository); + + metaService = app.get(MetaService) as jest.Mocked; + }); + + afterEach(async () => { + await Promise.all([ + app.get(DI.metasRepository).delete({}), + usersRepository.delete({}), + rolesRepository.delete({}), + roleAssignmentsRepository.delete({}), + ]); + await app.close(); + }); + + describe('getUserPolicies', () => { + test('instance default policies', async () => { + const user = await createUser(); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + } as any); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(false); + }); + + test('instance default policies 2', async () => { + const user = await createUser(); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: true, + }, + } as any); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(true); + }); + + test('with role', async () => { + const user = await createUser(); + const role = await createRole({ + name: 'a', + policies: { + canManageCustomEmojis: { + useDefault: false, + priority: 0, + value: true, + }, + }, + }); + await assign(role.id, user.id); + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + } as any); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.canManageCustomEmojis).toBe(true); + }); + + test('priority', async () => { + const user = await createUser(); + const role1 = await createRole({ + name: 'role1', + policies: { + driveCapacityMb: { + useDefault: false, + priority: 0, + value: 200, + }, + }, + }); + const role2 = await createRole({ + name: 'role2', + policies: { + driveCapacityMb: { + useDefault: false, + priority: 1, + value: 100, + }, + }, + }); + await assign(role1.id, user.id); + await assign(role2.id, user.id); + metaService.fetch.mockResolvedValue({ + policies: { + driveCapacityMb: 50, + }, + } as any); + + const result = await roleService.getUserPolicies(user.id); + + expect(result.driveCapacityMb).toBe(100); + }); + + test('conditional role', async () => { + const user1 = await createUser({ + createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + }); + const user2 = await createUser({ + createdAt: new Date(Date.now() - (1000 * 60 * 60 * 24 * 365)), + followersCount: 10, + }); + const role = await createRole({ + name: 'a', + policies: { + canManageCustomEmojis: { + useDefault: false, + priority: 0, + value: true, + }, + }, + target: 'conditional', + condFormula: { + type: 'and', + values: [{ + type: 'followersMoreThanOrEq', + value: 10, + }, { + type: 'createdMoreThan', + sec: 60 * 60 * 24 * 7, + }], + }, + }); + + metaService.fetch.mockResolvedValue({ + policies: { + canManageCustomEmojis: false, + }, + } as any); + + const user1Policies = await roleService.getUserPolicies(user1.id); + const user2Policies = await roleService.getUserPolicies(user2.id); + expect(user1Policies.canManageCustomEmojis).toBe(false); + expect(user2Policies.canManageCustomEmojis).toBe(true); + }); + }); +}); diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts index 036d0e19fd..1e9a51bc88 100644 --- a/packages/backend/test/unit/chart.ts +++ b/packages/backend/test/unit/chart.ts @@ -78,7 +78,7 @@ describe('Chart', () => { if (db) await db.destroy(); }); - it('Can updates', async () => { + test('Can updates', async () => { await testChart.increment(); await testChart.save(); @@ -102,7 +102,7 @@ describe('Chart', () => { }); }); - it('Can updates (dec)', async () => { + test('Can updates (dec)', async () => { await testChart.decrement(); await testChart.save(); @@ -126,7 +126,7 @@ describe('Chart', () => { }); }); - it('Empty chart', async () => { + test('Empty chart', async () => { const chartHours = await testChart.getChart('hour', 3, null); const chartDays = await testChart.getChart('day', 3, null); @@ -147,7 +147,7 @@ describe('Chart', () => { }); }); - it('Can updates at multiple times at same time', async () => { + test('Can updates at multiple times at same time', async () => { await testChart.increment(); await testChart.increment(); await testChart.increment(); @@ -173,7 +173,7 @@ describe('Chart', () => { }); }); - it('複数回saveされてもデータの更新は一度だけ', async () => { + test('複数回saveされてもデータの更新は一度だけ', async () => { await testChart.increment(); await testChart.save(); await testChart.save(); @@ -199,7 +199,7 @@ describe('Chart', () => { }); }); - it('Can updates at different times', async () => { + test('Can updates at different times', async () => { await testChart.increment(); await testChart.save(); @@ -230,7 +230,7 @@ describe('Chart', () => { // 仕様上はこうなってほしいけど、実装は難しそうなのでskip /* - it('Can updates at different times without save', async () => { + test('Can updates at different times without save', async () => { await testChart.increment(); clock.tick('01:00:00'); @@ -259,7 +259,7 @@ describe('Chart', () => { }); */ - it('Can padding', async () => { + test('Can padding', async () => { await testChart.increment(); await testChart.save(); @@ -289,7 +289,7 @@ describe('Chart', () => { }); // 要求された範囲にログがひとつもない場合でもパディングできる - it('Can padding from past range', async () => { + test('Can padding from past range', async () => { await testChart.increment(); await testChart.save(); @@ -317,7 +317,7 @@ describe('Chart', () => { // 要求された範囲の最も古い箇所に位置するログが存在しない場合でもパディングできる // Issue #3190 - it('Can padding from past range 2', async () => { + test('Can padding from past range 2', async () => { await testChart.increment(); await testChart.save(); @@ -346,7 +346,7 @@ describe('Chart', () => { }); }); - it('Can specify offset', async () => { + test('Can specify offset', async () => { await testChart.increment(); await testChart.save(); @@ -375,7 +375,7 @@ describe('Chart', () => { }); }); - it('Can specify offset (floor time)', async () => { + test('Can specify offset (floor time)', async () => { clock.tick('00:30:00'); await testChart.increment(); @@ -407,7 +407,7 @@ describe('Chart', () => { }); describe('Grouped', () => { - it('Can updates', async () => { + test('Can updates', async () => { await testGroupedChart.increment('alice'); await testGroupedChart.save(); @@ -451,7 +451,7 @@ describe('Chart', () => { }); describe('Unique increment', () => { - it('Can updates', async () => { + test('Can updates', async () => { await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('alice'); await testUniqueChart.uniqueIncrement('bob'); @@ -470,7 +470,7 @@ describe('Chart', () => { }); describe('Intersection', () => { - it('条件が満たされていない場合はカウントされない', async () => { + test('条件が満たされていない場合はカウントされない', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -492,7 +492,7 @@ describe('Chart', () => { }); }); - it('条件が満たされている場合にカウントされる', async () => { + test('条件が満たされている場合にカウントされる', async () => { await testIntersectionChart.addA('alice'); await testIntersectionChart.addA('bob'); await testIntersectionChart.addB('carol'); @@ -518,7 +518,7 @@ describe('Chart', () => { }); describe('Resync', () => { - it('Can resync', async () => { + test('Can resync', async () => { testChart.total = 1; await testChart.resync(); @@ -543,7 +543,7 @@ describe('Chart', () => { }); }); - it('Can resync (2)', async () => { + test('Can resync (2)', async () => { await testChart.increment(); await testChart.save(); diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 3e13e285da..40183d64ce 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -12,18 +12,18 @@ "@rollup/plugin-json": "6.0.0", "@rollup/pluginutils": "5.0.2", "@syuilo/aiscript": "0.12.4", - "@tabler/icons-webfont": "^2.1.2", + "@tabler/icons-webfont": "2.1.2", "@vitejs/plugin-vue": "4.0.0", - "@vue/compiler-sfc": "3.2.45", + "@vue/compiler-sfc": "3.2.47", "autobind-decorator": "2.4.0", "autosize": "5.0.2", "blurhash": "2.0.4", "broadcast-channel": "4.20.2", "browser-image-resizer": "git+https://github.com/misskey-dev/browser-image-resizer#v2.2.1-misskey.3", - "canvas-confetti": "^1.6.0", + "canvas-confetti": "1.6.0", "chart.js": "4.2.0", "chartjs-adapter-date-fns": "3.0.0", - "chartjs-chart-matrix": "^1.3.0", + "chartjs-chart-matrix": "1.3.0", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.0.0", "compare-versions": "5.0.1", @@ -31,23 +31,23 @@ "date-fns": "2.29.3", "escape-regexp": "0.0.1", "eventemitter3": "5.0.0", - "gsap": "^3.11.4", + "gsap": "3.11.4", "idb-keyval": "6.2.0", "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", "matter-js": "0.18.0", "mfm-js": "0.23.3", - "misskey-js": "0.0.14", - "photoswipe": "5.3.4", + "misskey-js": "0.0.15", + "photoswipe": "5.3.5", "prismjs": "1.29.0", "punycode": "2.3.0", "querystring": "0.2.1", "rndstr": "1.0.0", - "rollup": "3.11.0", + "rollup": "3.12.1", "s-age": "1.1.2", - "sanitize-html": "^2.8.1", - "sass": "1.57.1", + "sanitize-html": "2.9.0", + "sass": "1.58.0", "seedrandom": "3.0.5", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", @@ -59,11 +59,12 @@ "tsc-alias": "1.8.2", "tsconfig-paths": "4.1.2", "twemoji-parser": "14.0.0", - "typescript": "4.9.4", + "typescript": "4.9.5", "uuid": "9.0.0", "vanilla-tilt": "1.8.0", - "vite": "4.0.4", - "vue": "3.2.45", + "vue-plyr": "7.0.0", + "vite": "4.1.1", + "vue": "3.2.47", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" }, @@ -73,25 +74,25 @@ "@types/gulp": "4.0.10", "@types/gulp-rename": "2.0.1", "@types/matter-js": "0.18.2", - "@types/node": "^18.11.18", + "@types/node": "18.11.18", "@types/punycode": "2.1.0", - "@types/sanitize-html": "^2.8.0", + "@types/sanitize-html": "2.8.0", "@types/seedrandom": "3.0.4", "@types/throttle-debounce": "5.0.0", "@types/tinycolor2": "1.4.3", "@types/uuid": "9.0.0", "@types/websocket": "1.0.5", "@types/ws": "8.5.4", - "@typescript-eslint/eslint-plugin": "5.49.0", - "@typescript-eslint/parser": "5.49.0", - "@vue/runtime-core": "3.2.45", + "@typescript-eslint/eslint-plugin": "5.50.0", + "@typescript-eslint/parser": "5.50.0", + "@vue/runtime-core": "3.2.47", "cross-env": "7.0.3", - "cypress": "12.4.0", - "eslint": "8.32.0", + "cypress": "12.5.1", + "eslint": "8.33.0", "eslint-plugin-import": "2.27.5", "eslint-plugin-vue": "9.9.0", "start-server-and-test": "1.15.3", - "vue-eslint-parser": "^9.1.0", - "vue-tsc": "^1.0.24" + "vue-eslint-parser": "9.1.0", + "vue-tsc": "1.0.24" } } diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue index df0bf84116..5a2da050bb 100644 --- a/packages/frontend/src/components/MkMediaVideo.vue +++ b/packages/frontend/src/components/MkMediaVideo.vue @@ -6,19 +6,20 @@
- +
@@ -26,7 +27,9 @@