Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
46c258d77a | |||
3b5b3cf521 | |||
5e0bdd8a78 | |||
b299988bb5 | |||
e26bec6ab4 | |||
e9955e01d6 | |||
1974d8f58b | |||
08c0be11b2 | |||
87c7058494 | |||
b92addffa9 | |||
e8b49df842 | |||
18fd39b335 | |||
8a11322802 | |||
31929dad61 | |||
4a41d2fddc | |||
4c65b0cd6f | |||
3e89dc603d | |||
9595a56346 | |||
c7e8c27ce6 | |||
67792fcb5e | |||
353fc18f19 | |||
cf9e8ed39e | |||
8b71006fbe |
@ -1,6 +1,11 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
10.82.1
|
||||
----------
|
||||
* クラスタリング環境でのジョブキューの動作を修正
|
||||
* その他の軽微な改善
|
||||
|
||||
10.82.0
|
||||
----------
|
||||
* 自分の投稿情報をエクスポートできるように
|
||||
|
@ -61,6 +61,10 @@ Organize and store your files! Want to post a picture you have already uploaded?
|
||||
|
||||
...and more! Experience Misskey with your own eyes at [misskey.xyz](https://misskey.xyz) or join one of the [other instances](https://joinmisskey.github.io/) that are available.
|
||||
|
||||
:new: What's new
|
||||
----------------------------------------------------------------
|
||||
Please see the [Release notes](./CHANGELOG.md).
|
||||
|
||||
:package: Create your own instance
|
||||
----------------------------------------------------------------
|
||||
Please see the [Setup and Installation Guide](./docs/setup.en.md).
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "Email Address"
|
||||
email-verified: "Your email has been verified."
|
||||
email-not-verified: "Email address is not confirmed. Please check your inbox."
|
||||
export: "Export"
|
||||
export-notes: "Export all of your Notes"
|
||||
export-requested: "You have requested an export. This may take a while. After the export is complete, the resulting file will be added to the drive."
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "User"
|
||||
rename: "Rename list"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "Correo electrónico"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Usuarios"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "Adresse de courrier électronique"
|
||||
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
||||
email-not-verified: "Adresse de courriel n’est pas confirmée. Veuillez vérifier votre boite de réception."
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Utilisateur·rice"
|
||||
rename: "Renommer la liste"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "このメールアドレスOKや!"
|
||||
email-not-verified: "メールアドレスが確認されとらん。メールボックスもっぺん見てくれへん?"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "메일 주소"
|
||||
email-verified: "매일 주소가 확인되었습니다"
|
||||
email-not-verified: "메일 주소가 확인되지 않았습니다. 받은 편지함을 확인하여 주시기 바랍니다."
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "사용자"
|
||||
rename: "리스트 이름 바꾸기"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "Adres e-mail"
|
||||
email-verified: "Twój adres e-mail został zweryfikowany."
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Użytkownicy"
|
||||
rename: "Zmień nazwę listy"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
email-not-verified: "メールアドレスが確認されていません。メールボックスをご確認ください。"
|
||||
export: "エクスポート"
|
||||
export-notes: "すべての投稿のエクスポート"
|
||||
export-requested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、ドライブにファイルが追加されます。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
rename: "リスト名を変更"
|
||||
|
@ -509,6 +509,9 @@ common/views/components/profile-editor.vue:
|
||||
email-address: "电子邮件地址"
|
||||
email-verified: "电子邮件地址已验证"
|
||||
email-not-verified: "邮件地址尚未验证。 请检查您的邮箱。"
|
||||
export: "导出"
|
||||
export-notes: "导出所有帖子"
|
||||
export-requested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "用户"
|
||||
rename: "重命名列表"
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "10.82.0",
|
||||
"clientVersion": "2.0.14114",
|
||||
"version": "10.82.1",
|
||||
"clientVersion": "2.0.14137",
|
||||
"codename": "nighthike",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -70,7 +70,7 @@
|
||||
"@types/mkdirp": "0.5.2",
|
||||
"@types/mocha": "5.2.5",
|
||||
"@types/mongodb": "3.1.19",
|
||||
"@types/node": "10.12.18",
|
||||
"@types/node": "10.12.21",
|
||||
"@types/nodemailer": "4.6.5",
|
||||
"@types/nprogress": "0.0.29",
|
||||
"@types/oauth": "0.9.1",
|
||||
@ -147,9 +147,8 @@
|
||||
"insert-text-at-cursor": "0.1.1",
|
||||
"is-root": "2.0.0",
|
||||
"is-svg": "3.0.0",
|
||||
"is-url": "1.2.4",
|
||||
"js-yaml": "3.12.1",
|
||||
"jsdom": "13.1.0",
|
||||
"jsdom": "13.2.0",
|
||||
"json5": "2.1.0",
|
||||
"json5-loader": "1.0.1",
|
||||
"katex": "0.10.0",
|
||||
|
@ -5,11 +5,15 @@ program
|
||||
.version(pkg.version)
|
||||
.option('--no-daemons', 'Disable daemon processes (for debbuging)')
|
||||
.option('--disable-clustering', 'Disable clustering')
|
||||
.option('--disable-queue', 'Disable job queue')
|
||||
.option('--disable-queue', 'Disable job queue processing')
|
||||
.option('--only-queue', 'Pocessing job queue only')
|
||||
.option('--quiet', 'Suppress all logs')
|
||||
.option('--verbose', 'Enable all logs')
|
||||
.option('--slow', 'Delay all requests (for debbuging)')
|
||||
.option('--color', 'This option is a dummy for some external program\'s (e.g. forever) issue.')
|
||||
.parse(process.argv);
|
||||
|
||||
if (process.env.MK_DISABLE_QUEUE) program.disableQueue = true;
|
||||
if (process.env.MK_ONLY_QUEUE) program.onlyQueue = true;
|
||||
|
||||
export { program };
|
||||
|
@ -119,11 +119,11 @@ export default Vue.extend({
|
||||
font-size 16px
|
||||
cursor pointer
|
||||
transition inherit
|
||||
color var(--text)
|
||||
|
||||
> span
|
||||
display block
|
||||
line-height 20px
|
||||
color var(--text)
|
||||
transition inherit
|
||||
|
||||
> p
|
||||
|
@ -6,7 +6,6 @@ import * as fs from 'fs';
|
||||
import { URL } from 'url';
|
||||
import * as yaml from 'js-yaml';
|
||||
import { Source, Mixin } from './types';
|
||||
import isUrl = require('is-url');
|
||||
import * as pkg from '../../package.json';
|
||||
|
||||
/**
|
||||
@ -26,10 +25,7 @@ export default function load() {
|
||||
|
||||
const mixin = {} as Mixin;
|
||||
|
||||
// Validate URLs
|
||||
if (!isUrl(config.url)) throw `url="${config.url}" is not a valid URL`;
|
||||
|
||||
const url = new URL(config.url);
|
||||
const url = validateUrl(config.url);
|
||||
config.url = normalizeUrl(config.url);
|
||||
|
||||
mixin.host = url.host;
|
||||
@ -51,6 +47,21 @@ export default function load() {
|
||||
return Object.assign(config, mixin);
|
||||
}
|
||||
|
||||
function tryCreateUrl(url: string) {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (e) {
|
||||
throw `url="${url}" is not a valid URL.`;
|
||||
}
|
||||
}
|
||||
|
||||
function validateUrl(url: string) {
|
||||
const result = tryCreateUrl(url);
|
||||
if (result.pathname.replace('/', '').length) throw `url="${url}" is not a valid URL, has a pathname.`;
|
||||
if (!url.includes(result.host)) throw `url="${url}" is not a valid URL, has an invalid hostname.`;
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeUrl(url: string) {
|
||||
return url.endsWith('/') ? url.substr(0, url.length - 1) : url;
|
||||
}
|
||||
|
80
src/index.ts
80
src/index.ts
@ -35,6 +35,11 @@ const ev = new Xev();
|
||||
function main() {
|
||||
process.title = `Misskey (${cluster.isMaster ? 'master' : 'worker'})`;
|
||||
|
||||
if (program.onlyQueue) {
|
||||
queueMain();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cluster.isMaster || program.disableClustering) {
|
||||
masterMain();
|
||||
|
||||
@ -53,12 +58,7 @@ function main() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init master process
|
||||
*/
|
||||
async function masterMain() {
|
||||
let config: Config;
|
||||
|
||||
function greet() {
|
||||
if (!program.quiet) {
|
||||
//#region Misskey logo
|
||||
const v = `v${pkg.version}`;
|
||||
@ -75,10 +75,34 @@ async function masterMain() {
|
||||
bootLogger.info('Welcome to Misskey!');
|
||||
bootLogger.info(`Misskey v${pkg.version}`, true);
|
||||
bootLogger.info('Misskey is maintained by @syuilo, @AyaMorisawa, @mei23, and @acid-chicken.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Init master process
|
||||
*/
|
||||
async function masterMain() {
|
||||
greet();
|
||||
|
||||
let config: Config;
|
||||
|
||||
try {
|
||||
// initialize app
|
||||
config = await init();
|
||||
|
||||
if (config.port == null) {
|
||||
bootLogger.error('The port is not configured. Please configure port.', true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
|
||||
bootLogger.error('You need root privileges to listen on well-known port on Linux', true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!await isPortAvailable(config.port)) {
|
||||
bootLogger.error(`Port ${config.port} is already in use`, true);
|
||||
process.exit(1);
|
||||
}
|
||||
} catch (e) {
|
||||
bootLogger.error('Fatal error occurred during initialization', true);
|
||||
process.exit(1);
|
||||
@ -90,6 +114,9 @@ async function masterMain() {
|
||||
await spawnWorkers(config.clusterLimit);
|
||||
}
|
||||
|
||||
// start queue
|
||||
require('./queue').default();
|
||||
|
||||
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, true);
|
||||
}
|
||||
|
||||
@ -100,15 +127,35 @@ async function workerMain() {
|
||||
// start server
|
||||
await require('./server').default();
|
||||
|
||||
// start processor
|
||||
require('./queue').default();
|
||||
|
||||
if (cluster.isWorker) {
|
||||
// Send a 'ready' message to parent process
|
||||
process.send('ready');
|
||||
}
|
||||
}
|
||||
|
||||
async function queueMain() {
|
||||
greet();
|
||||
|
||||
try {
|
||||
// initialize app
|
||||
await init();
|
||||
} catch (e) {
|
||||
bootLogger.error('Fatal error occurred during initialization', true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
bootLogger.succ('Misskey initialized');
|
||||
|
||||
// start processor
|
||||
const queue = require('./queue').default();
|
||||
|
||||
if (queue) {
|
||||
bootLogger.succ('Queue started', true);
|
||||
} else {
|
||||
bootLogger.error('Queue not available');
|
||||
}
|
||||
}
|
||||
|
||||
const runningNodejsVersion = process.version.slice(1).split('.').map(x => parseInt(x, 10));
|
||||
const requiredNodejsVersion = [10, 0, 0];
|
||||
const satisfyNodejsVersion = !lessThan(runningNodejsVersion, requiredNodejsVersion);
|
||||
@ -170,21 +217,6 @@ async function init(): Promise<Config> {
|
||||
|
||||
configLogger.succ('Loaded');
|
||||
|
||||
if (config.port == null) {
|
||||
bootLogger.error('The port is not configured. Please configure port.', true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.platform === 'linux' && isWellKnownPort(config.port) && !isRoot()) {
|
||||
bootLogger.error('You need root privileges to listen on well-known port on Linux', true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!await isPortAvailable(config.port)) {
|
||||
bootLogger.error(`Port ${config.port} is already in use`, true);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Try to connect to MongoDB
|
||||
try {
|
||||
await checkMongoDB(config, bootLogger);
|
||||
|
@ -142,7 +142,7 @@ export const mfmLanguage = P.createLanguage({
|
||||
},
|
||||
hashtag: () => P((input, i) => {
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^#([^\s\.,!\?'"#:]+)/i);
|
||||
const match = text.match(/^#([^\s\.,!\?'"#:\/]+)/i);
|
||||
if (!match) return P.makeFailure(i, 'not a hashtag');
|
||||
let hashtag = match[1];
|
||||
hashtag = removeOrphanedBrackets(hashtag);
|
||||
|
@ -45,7 +45,7 @@ export default class Logger {
|
||||
this.log(important ? chalk.bgGreen.white('DONE') : chalk.green('DONE'), chalk.green(message), important);
|
||||
}
|
||||
|
||||
public debug(message: string, important = false): void { // デバッグ用に使う(開発者にとっては必要だが利用者にとっては不要な情報)
|
||||
public debug(message: string, important = false): void { // デバッグ用に使う(開発者に必要だが利用者に不要な情報)
|
||||
if (process.env.NODE_ENV != 'production' || program.verbose) {
|
||||
this.log(chalk.gray('VERB'), chalk.gray(message), important);
|
||||
}
|
||||
|
20
src/prelude/maybe.ts
Normal file
20
src/prelude/maybe.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export interface Maybe<T> {
|
||||
isJust(): this is Just<T>;
|
||||
}
|
||||
|
||||
export type Just<T> = Maybe<T> & {
|
||||
get(): T
|
||||
};
|
||||
|
||||
export function just<T>(value: T): Just<T> {
|
||||
return {
|
||||
isJust: () => true,
|
||||
get: () => value
|
||||
};
|
||||
}
|
||||
|
||||
export function nothing<T>(): Maybe<T> {
|
||||
return {
|
||||
isJust: () => false,
|
||||
};
|
||||
}
|
@ -4,13 +4,15 @@ import config from '../config';
|
||||
import { ILocalUser } from '../models/user';
|
||||
import { program } from '../argv';
|
||||
import handler from './processors';
|
||||
import { queueLogger } from './logger';
|
||||
|
||||
const enableQueue = config.redis != null && !program.disableQueue;
|
||||
const enableQueue = !program.disableQueue;
|
||||
const queueAvailable = config.redis != null;
|
||||
|
||||
const queue = initializeQueue();
|
||||
|
||||
function initializeQueue() {
|
||||
if (enableQueue) {
|
||||
if (queueAvailable) {
|
||||
return new Queue('misskey', {
|
||||
redis: {
|
||||
port: config.redis.port,
|
||||
@ -30,7 +32,7 @@ function initializeQueue() {
|
||||
}
|
||||
|
||||
export function createHttpJob(data: any) {
|
||||
if (enableQueue) {
|
||||
if (queueAvailable) {
|
||||
return queue.createJob(data)
|
||||
.retries(4)
|
||||
.backoff('exponential', 16384) // 16s
|
||||
@ -52,7 +54,7 @@ export function deliver(user: ILocalUser, content: any, to: any) {
|
||||
}
|
||||
|
||||
export function createExportNotesJob(user: ILocalUser) {
|
||||
if (!enableQueue) throw 'queue disabled';
|
||||
if (!queueAvailable) throw 'queue unavailable';
|
||||
|
||||
return queue.createJob({
|
||||
type: 'exportNotes',
|
||||
@ -62,7 +64,10 @@ export function createExportNotesJob(user: ILocalUser) {
|
||||
}
|
||||
|
||||
export default function() {
|
||||
if (enableQueue) {
|
||||
if (queueAvailable && enableQueue) {
|
||||
queue.process(128, handler);
|
||||
queueLogger.succ('Processing started');
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
@ -19,16 +19,20 @@ export default class Resolver {
|
||||
: value;
|
||||
|
||||
switch (collection.type) {
|
||||
case 'Collection':
|
||||
collection.objects = collection.items;
|
||||
break;
|
||||
case 'Collection': {
|
||||
collection.objects = collection.items;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'OrderedCollection':
|
||||
collection.objects = collection.orderedItems;
|
||||
break;
|
||||
case 'OrderedCollection': {
|
||||
collection.objects = collection.orderedItems;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`unknown collection type: ${collection.type}`);
|
||||
default: {
|
||||
logger.error(`unknown collection type: ${collection.type}`);
|
||||
throw new Error(`unknown collection type: ${collection.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
@ -36,6 +40,7 @@ export default class Resolver {
|
||||
|
||||
public async resolve(value: any): Promise<IObject> {
|
||||
if (value == null) {
|
||||
logger.error('resolvee is null (or undefined)');
|
||||
throw new Error('resolvee is null (or undefined)');
|
||||
}
|
||||
|
||||
@ -44,6 +49,7 @@ export default class Resolver {
|
||||
}
|
||||
|
||||
if (this.history.has(value)) {
|
||||
logger.error(`cannot resolve already resolved one`);
|
||||
throw new Error('cannot resolve already resolved one');
|
||||
}
|
||||
|
||||
@ -59,6 +65,7 @@ export default class Resolver {
|
||||
},
|
||||
json: true
|
||||
}).catch(e => {
|
||||
logger.error(`request error: ${e.message}`);
|
||||
throw new Error(`request error: ${e.message}`);
|
||||
});
|
||||
|
||||
|
@ -42,7 +42,12 @@ export async function proxyMedia(ctx: Koa.BaseContext) {
|
||||
ctx.body = image.data;
|
||||
} catch (e) {
|
||||
serverLogger.error(e);
|
||||
ctx.status = 500;
|
||||
|
||||
if (typeof e == 'number' && e >= 400 && e < 500) {
|
||||
ctx.status = e;
|
||||
} else {
|
||||
ctx.status = 500;
|
||||
}
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
|
@ -611,6 +611,14 @@ describe('MFM', () => {
|
||||
text('(#123)'),
|
||||
]);
|
||||
});
|
||||
|
||||
it('ignore slash', () => {
|
||||
const tokens = parse('#foo/bar');
|
||||
assert.deepStrictEqual(tokens, [
|
||||
leaf('hashtag', { hashtag: 'foo' }),
|
||||
text('/bar'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('quote', () => {
|
||||
|
28
test/prelude/maybe.ts
Normal file
28
test/prelude/maybe.ts
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Tests of Maybe
|
||||
*
|
||||
* How to run the tests:
|
||||
* > mocha test/prelude/maybe.ts --require ts-node/register
|
||||
*
|
||||
* To specify test:
|
||||
* > mocha test/prelude/maybe.ts --require ts-node/register -g 'test name'
|
||||
*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { just, nothing } from '../../src/prelude/maybe';
|
||||
|
||||
describe('just', () => {
|
||||
it('has a value', () => {
|
||||
assert.deepStrictEqual(just(3).isJust(), true);
|
||||
});
|
||||
|
||||
it('has the inverse called get', () => {
|
||||
assert.deepStrictEqual(just(3).get(), 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nothing', () => {
|
||||
it('has no value', () => {
|
||||
assert.deepStrictEqual(nothing().isJust(), false);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user