parent
							
								
									1809800980
								
							
						
					
					
						commit
						3f8ebac466
					
				
					 46 changed files with 244 additions and 225 deletions
				
			
		|  | @ -22,5 +22,5 @@ elasticsearch: | ||||||
|   port: 9200 |   port: 9200 | ||||||
|   pass: '' |   pass: '' | ||||||
| recaptcha: | recaptcha: | ||||||
|   siteKey: hima |   site_key: hima | ||||||
|   secretKey: saku |   secret_key: saku | ||||||
|  |  | ||||||
|  | @ -22,5 +22,5 @@ elasticsearch: | ||||||
|   port: 9200 |   port: 9200 | ||||||
|   pass: '' |   pass: '' | ||||||
| recaptcha: | recaptcha: | ||||||
|   siteKey: hima |   site_key: hima | ||||||
|   secretKey: saku |   secret_key: saku | ||||||
|  |  | ||||||
							
								
								
									
										55
									
								
								docs/config.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								docs/config.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | ||||||
|  | ``` yaml | ||||||
|  | # サーバーのメンテナ情報 | ||||||
|  | maintainer: | ||||||
|  |   # メンテナの名前 | ||||||
|  |   name: | ||||||
|  | 
 | ||||||
|  |   # メンテナの連絡先(URLかmailto形式のURL) | ||||||
|  |   url: | ||||||
|  | 
 | ||||||
|  | # プライマリURL | ||||||
|  | url: | ||||||
|  | 
 | ||||||
|  | # セカンダリURL | ||||||
|  | secondary_url: | ||||||
|  | 
 | ||||||
|  | # 待受ポート | ||||||
|  | port: | ||||||
|  | 
 | ||||||
|  | # TLSの設定 | ||||||
|  | https: | ||||||
|  |   # TLSを有効にするか否か | ||||||
|  |   enable: false | ||||||
|  | 
 | ||||||
|  |   key: null | ||||||
|  |   cert: null | ||||||
|  |   ca: null | ||||||
|  | 
 | ||||||
|  | # MongoDBの設定 | ||||||
|  | mongodb: | ||||||
|  |   host: localhost | ||||||
|  |   port: 27017 | ||||||
|  |   db: misskey | ||||||
|  |   user: | ||||||
|  |   pass: | ||||||
|  | 
 | ||||||
|  | # Redisの設定 | ||||||
|  | redis: | ||||||
|  |   host: localhost | ||||||
|  |   port: 6379 | ||||||
|  |   pass: | ||||||
|  | 
 | ||||||
|  | # reCAPTCHAの設定 | ||||||
|  | recaptcha: | ||||||
|  |   site_key: | ||||||
|  |   secret_key: | ||||||
|  | 
 | ||||||
|  | # ServiceWrokerの設定 | ||||||
|  | sw: | ||||||
|  |   # VAPIDの公開鍵 | ||||||
|  |   public_key: | ||||||
|  | 
 | ||||||
|  |   # VAPIDの秘密鍵 | ||||||
|  |   private_key: | ||||||
|  | 
 | ||||||
|  | ``` | ||||||
|  | @ -36,6 +36,15 @@ Note that Misskey uses following subdomains: | ||||||
| Misskey requires reCAPTCHA tokens. | Misskey requires reCAPTCHA tokens. | ||||||
| Please visit https://www.google.com/recaptcha/intro/ and generate keys. | Please visit https://www.google.com/recaptcha/intro/ and generate keys. | ||||||
| 
 | 
 | ||||||
|  | *(optional)* Generating VAPID keys | ||||||
|  | ---------------------------------------------------------------- | ||||||
|  | If you want to enable ServiceWroker, you need to generate VAPID keys: | ||||||
|  | 
 | ||||||
|  | ``` shell | ||||||
|  | npm install web-push -g | ||||||
|  | web-push generate-vapid-keys | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| *3.* Install dependencies | *3.* Install dependencies | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| Please install and setup these softwares: | Please install and setup these softwares: | ||||||
|  | @ -51,24 +60,6 @@ Please install and setup these softwares: | ||||||
| 
 | 
 | ||||||
| *4.* Install Misskey | *4.* Install Misskey | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| There is **two ways** to install Misskey: |  | ||||||
| 
 |  | ||||||
| ### WAY 1) Using built code (recommended) |  | ||||||
| We have the official release of Misskey. |  | ||||||
| The built code is automatically pushed to https://github.com/syuilo/misskey/tree/release after the CI test succeeds. |  | ||||||
| 
 |  | ||||||
| 1. `git clone -b release git://github.com/syuilo/misskey.git` |  | ||||||
| 2. `cd misskey` |  | ||||||
| 3. `npm install` |  | ||||||
| 
 |  | ||||||
| #### Update |  | ||||||
| 1. `git fetch` |  | ||||||
| 2. `git reset --hard origin/release` |  | ||||||
| 3. `npm install` |  | ||||||
| 
 |  | ||||||
| ### WAY 2) Using source code |  | ||||||
| If you want to build Misskey manually, you can do it via the |  | ||||||
| `build` command after download the source code of Misskey and install dependencies: |  | ||||||
| 
 | 
 | ||||||
| 1. `git clone -b master git://github.com/syuilo/misskey.git` | 1. `git clone -b master git://github.com/syuilo/misskey.git` | ||||||
| 2. `cd misskey` | 2. `cd misskey` | ||||||
|  |  | ||||||
|  | @ -37,6 +37,15 @@ Misskeyは以下のサブドメインを使います: | ||||||
| MisskeyはreCAPTCHAトークンを必要とします。 | MisskeyはreCAPTCHAトークンを必要とします。 | ||||||
| https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生成してください。 | https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生成してください。 | ||||||
| 
 | 
 | ||||||
|  | *(オプション)* VAPIDキーペアの生成 | ||||||
|  | ---------------------------------------------------------------- | ||||||
|  | ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります: | ||||||
|  | 
 | ||||||
|  | ``` shell | ||||||
|  | npm install web-push -g | ||||||
|  | web-push generate-vapid-keys | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
| *3.* 依存関係をインストールする | *3.* 依存関係をインストールする | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| これらのソフトウェアをインストール・設定してください: | これらのソフトウェアをインストール・設定してください: | ||||||
|  | @ -52,26 +61,6 @@ https://www.google.com/recaptcha/intro/ にアクセスしてトークンを生 | ||||||
| 
 | 
 | ||||||
| *4.* Misskeyのインストール | *4.* Misskeyのインストール | ||||||
| ---------------------------------------------------------------- | ---------------------------------------------------------------- | ||||||
| Misskeyをインストールするには**2つの方法**があります: |  | ||||||
| 
 |  | ||||||
| ### 方法 1) ビルドされたコードを利用する (推奨) |  | ||||||
| Misskeyには公式のリリースがあります。 |  | ||||||
| ビルドされたコードはCIテストに合格した後、自動で https://github.com/syuilo/misskey/tree/release にpushされています。 |  | ||||||
| 
 |  | ||||||
| 1. `git clone -b release git://github.com/syuilo/misskey.git` |  | ||||||
| 2. `cd misskey` |  | ||||||
| 3. `npm install` |  | ||||||
| 
 |  | ||||||
| #### アップデートするには: |  | ||||||
| 1. `git fetch` |  | ||||||
| 2. `git reset --hard origin/release` |  | ||||||
| 3. `npm install` |  | ||||||
| 
 |  | ||||||
| ### 方法 2) ソースコードを利用する |  | ||||||
| > 注: この方法では正しくビルド・動作できることは保証されません。 |  | ||||||
| 
 |  | ||||||
| Misskeyを手動でビルドしたい場合は、Misskeyのソースコードと依存関係をインストールした後、 |  | ||||||
| `build`コマンドを用いることができます: |  | ||||||
| 
 | 
 | ||||||
| 1. `git clone -b master git://github.com/syuilo/misskey.git` | 1. `git clone -b master git://github.com/syuilo/misskey.git` | ||||||
| 2. `cd misskey` | 2. `cd misskey` | ||||||
|  |  | ||||||
|  | @ -4,7 +4,11 @@ import Subscription from '../models/sw-subscription'; | ||||||
| import config from '../../conf'; | import config from '../../conf'; | ||||||
| 
 | 
 | ||||||
| if (config.sw) { | if (config.sw) { | ||||||
| 	push.setGCMAPIKey(config.sw.gcm_api_key); | 	// アプリケーションの連絡先と、サーバーサイドの鍵ペアの情報を登録
 | ||||||
|  | 	push.setVapidDetails( | ||||||
|  | 		config.maintainer.url, | ||||||
|  | 		config.sw.public_key, | ||||||
|  | 		config.sw.private_key); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function(userId: mongo.ObjectID | string, type, body?) { | export default async function(userId: mongo.ObjectID | string, type, body?) { | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ import generateUserToken from '../common/generate-native-user-token'; | ||||||
| import config from '../../conf'; | import config from '../../conf'; | ||||||
| 
 | 
 | ||||||
| recaptcha.init({ | recaptcha.init({ | ||||||
| 	secret_key: config.recaptcha.secretKey | 	secret_key: config.recaptcha.secret_key | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const home = { | const home = { | ||||||
|  |  | ||||||
|  | @ -3,7 +3,6 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import * as fs from 'fs'; | import * as fs from 'fs'; | ||||||
| import * as URL from 'url'; |  | ||||||
| import * as yaml from 'js-yaml'; | import * as yaml from 'js-yaml'; | ||||||
| import isUrl = require('is-url'); | import isUrl = require('is-url'); | ||||||
| 
 | 
 | ||||||
|  | @ -23,7 +22,19 @@ export const path = process.env.NODE_ENV == 'test' | ||||||
|  * ユーザーが設定する必要のある情報 |  * ユーザーが設定する必要のある情報 | ||||||
|  */ |  */ | ||||||
| type Source = { | type Source = { | ||||||
| 	maintainer: string; | 	/** | ||||||
|  | 	 * メンテナ情報 | ||||||
|  | 	 */ | ||||||
|  | 	maintainer: { | ||||||
|  | 		/** | ||||||
|  | 		 * メンテナの名前 | ||||||
|  | 		 */ | ||||||
|  | 		name: string; | ||||||
|  | 		/** | ||||||
|  | 		 * メンテナの連絡先(URLかmailto形式のURL) | ||||||
|  | 		 */ | ||||||
|  | 		url: string; | ||||||
|  | 	}; | ||||||
| 	url: string; | 	url: string; | ||||||
| 	secondary_url: string; | 	secondary_url: string; | ||||||
| 	port: number; | 	port: number; | ||||||
|  | @ -52,8 +63,8 @@ type Source = { | ||||||
| 		pass: string; | 		pass: string; | ||||||
| 	}; | 	}; | ||||||
| 	recaptcha: { | 	recaptcha: { | ||||||
| 		siteKey: string; | 		site_key: string; | ||||||
| 		secretKey: string; | 		secret_key: string; | ||||||
| 	}; | 	}; | ||||||
| 	accesslog?: string; | 	accesslog?: string; | ||||||
| 	accesses?: { | 	accesses?: { | ||||||
|  | @ -80,8 +91,8 @@ type Source = { | ||||||
| 	 * Service Worker | 	 * Service Worker | ||||||
| 	 */ | 	 */ | ||||||
| 	sw?: { | 	sw?: { | ||||||
| 		gcm_sender_id: string; | 		public_key: string; | ||||||
| 		gcm_api_key: string; | 		private_key: string; | ||||||
| 	}; | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -114,14 +125,6 @@ export default function load() { | ||||||
| 	if (!isUrl(config.url)) urlError(config.url); | 	if (!isUrl(config.url)) urlError(config.url); | ||||||
| 	if (!isUrl(config.secondary_url)) urlError(config.secondary_url); | 	if (!isUrl(config.secondary_url)) urlError(config.secondary_url); | ||||||
| 
 | 
 | ||||||
| 	const url = URL.parse(config.url); |  | ||||||
| 	const head = url.host.split('.')[0]; |  | ||||||
| 
 |  | ||||||
| 	if (head != 'misskey' && head != 'localhost') { |  | ||||||
| 		console.error(`プライマリドメインは、必ず「misskey」ドメインで始まっていなければなりません(現在の設定では「${head}」で始まっています)。例えば「https://misskey.xyz」「http://misskey.my.app.example.com」などが正しいプライマリURLです。`); |  | ||||||
| 		process.exit(); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	config.url = normalizeUrl(config.url); | 	config.url = normalizeUrl(config.url); | ||||||
| 	config.secondary_url = normalizeUrl(config.secondary_url); | 	config.secondary_url = normalizeUrl(config.secondary_url); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -26,11 +26,11 @@ | ||||||
| 		<hr> | 		<hr> | ||||||
| 		<mk-channel-form if={ SIGNIN } channel={ channel } ref="form"/> | 		<mk-channel-form if={ SIGNIN } channel={ channel } ref="form"/> | ||||||
| 		<div if={ !SIGNIN }> | 		<div if={ !SIGNIN }> | ||||||
| 			<p>参加するには<a href={ CONFIG.url }>ログインまたは新規登録</a>してください</p> | 			<p>参加するには<a href={ _URL_ }>ログインまたは新規登録</a>してください</p> | ||||||
| 		</div> | 		</div> | ||||||
| 		<hr> | 		<hr> | ||||||
| 		<footer> | 		<footer> | ||||||
| 			<small><a href={ CONFIG.url }>Misskey</a> ver { version } (葵 aoi)</small> | 			<small><a href={ _URL_ }>Misskey</a> ver { _VERSION_ } (葵 aoi)</small> | ||||||
| 		</footer> | 		</footer> | ||||||
| 	</main> | 	</main> | ||||||
| 	<style> | 	<style> | ||||||
|  | @ -66,7 +66,6 @@ | ||||||
| 		this.channel = null; | 		this.channel = null; | ||||||
| 		this.posts = null; | 		this.posts = null; | ||||||
| 		this.connection = new ChannelStream(this.id); | 		this.connection = new ChannelStream(this.id); | ||||||
| 		this.version = VERSION; |  | ||||||
| 		this.unreadCount = 0; | 		this.unreadCount = 0; | ||||||
| 
 | 
 | ||||||
| 		this.on('mount', () => { | 		this.on('mount', () => { | ||||||
|  | @ -166,7 +165,7 @@ | ||||||
| <mk-channel-post> | <mk-channel-post> | ||||||
| 	<header> | 	<header> | ||||||
| 		<a class="index" onclick={ reply }>{ post.index }:</a> | 		<a class="index" onclick={ reply }>{ post.index }:</a> | ||||||
| 		<a class="name" href={ CONFIG.url + '/' + post.user.username }><b>{ post.user.name }</b></a> | 		<a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a> | ||||||
| 		<mk-time time={ post.created_at }/> | 		<mk-time time={ post.created_at }/> | ||||||
| 		<mk-time time={ post.created_at } mode="detail"/> | 		<mk-time time={ post.created_at } mode="detail"/> | ||||||
| 		<span>ID:<i>{ post.user.username }</i></span> | 		<span>ID:<i>{ post.user.username }</i></span> | ||||||
|  | @ -284,8 +283,6 @@ | ||||||
| 
 | 
 | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		import CONFIG from '../../common/scripts/config'; |  | ||||||
| 
 |  | ||||||
| 		this.mixin('api'); | 		this.mixin('api'); | ||||||
| 
 | 
 | ||||||
| 		this.channel = this.opts.channel; | 		this.channel = this.opts.channel; | ||||||
|  | @ -357,7 +354,7 @@ | ||||||
| 				}); | 				}); | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 			window.open(CONFIG.url + '/selectdrive?multiple=true', | 			window.open(_URL_ + '/selectdrive?multiple=true', | ||||||
| 				'drive_window', | 				'drive_window', | ||||||
| 				'height=500,width=800'); | 				'height=500,width=800'); | ||||||
| 		}; | 		}; | ||||||
|  | @ -390,7 +387,7 @@ | ||||||
| </mk-twitter-button> | </mk-twitter-button> | ||||||
| 
 | 
 | ||||||
| <mk-line-button> | <mk-line-button> | ||||||
| 	<div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ CONFIG.chUrl } style="display: none;"></div> | 	<div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ _CH_URL_ } style="display: none;"></div> | ||||||
| 	<script> | 	<script> | ||||||
| 		this.on('mount', () => { | 		this.on('mount', () => { | ||||||
| 			const head = document.getElementsByTagName('head')[0]; | 			const head = document.getElementsByTagName('head')[0]; | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| <mk-header> | <mk-header> | ||||||
| 	<div> | 	<div> | ||||||
| 		<a href={ CONFIG.chUrl }>Index</a> | <a href={ CONFIG.url }>Misskey</a> | 		<a href={ _CH_URL_ }>Index</a> | <a href={ _URL_ }>Misskey</a> | ||||||
| 	</div> | 	</div> | ||||||
| 	<div> | 	<div> | ||||||
| 		<a if={ !SIGNIN } href={ CONFIG.url }>ログイン(新規登録)</a> | 		<a if={ !SIGNIN } href={ _URL_ }>ログイン(新規登録)</a> | ||||||
| 		<a if={ SIGNIN } href={ CONFIG.url + '/' + I.username }>{ I.username }</a> | 		<a if={ SIGNIN } href={ _URL_ + '/' + I.username }>{ I.username }</a> | ||||||
| 	</div> | 	</div> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
|  |  | ||||||
|  | @ -3,11 +3,12 @@ import * as riot from 'riot'; | ||||||
| import signout from './scripts/signout'; | import signout from './scripts/signout'; | ||||||
| import Progress from './scripts/loading'; | import Progress from './scripts/loading'; | ||||||
| import HomeStreamManager from './scripts/streaming/home-stream-manager'; | import HomeStreamManager from './scripts/streaming/home-stream-manager'; | ||||||
| import CONFIG from './scripts/config'; |  | ||||||
| import api from './scripts/api'; | import api from './scripts/api'; | ||||||
| 
 | 
 | ||||||
| declare var VERSION: string; | declare const _VERSION_: string; | ||||||
| declare var LANG: string; | declare const _LANG_: string; | ||||||
|  | declare const _API_URL_: string; | ||||||
|  | declare const _SW_PUBLICKEY_: string; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Misskey Operating System |  * Misskey Operating System | ||||||
|  | @ -113,7 +114,7 @@ export default class MiOS extends EventEmitter { | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// Fetch user
 | 			// Fetch user
 | ||||||
| 			fetch(`${CONFIG.apiUrl}/i`, { | 			fetch(`${_API_URL_}/i`, { | ||||||
| 				method: 'POST', | 				method: 'POST', | ||||||
| 				body: JSON.stringify({ | 				body: JSON.stringify({ | ||||||
| 					i: token | 					i: token | ||||||
|  | @ -229,10 +230,15 @@ export default class MiOS extends EventEmitter { | ||||||
| 			this.swRegistration = registration; | 			this.swRegistration = registration; | ||||||
| 
 | 
 | ||||||
| 			// Options of pushManager.subscribe
 | 			// Options of pushManager.subscribe
 | ||||||
|  | 			// SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters
 | ||||||
| 			const opts = { | 			const opts = { | ||||||
| 				// A boolean indicating that the returned push subscription
 | 				// A boolean indicating that the returned push subscription
 | ||||||
| 				// will only be used for messages whose effect is made visible to the user.
 | 				// will only be used for messages whose effect is made visible to the user.
 | ||||||
| 				userVisibleOnly: true | 				userVisibleOnly: true, | ||||||
|  | 
 | ||||||
|  | 				// A public key your push server will use to send
 | ||||||
|  | 				// messages to client apps via a push server.
 | ||||||
|  | 				applicationServerKey: urlBase64ToUint8Array(_SW_PUBLICKEY_) | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 			// Subscribe push notification
 | 			// Subscribe push notification
 | ||||||
|  | @ -257,7 +263,7 @@ export default class MiOS extends EventEmitter { | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		// The path of service worker script
 | 		// The path of service worker script
 | ||||||
| 		const sw = `/sw.${VERSION}.${LANG}.js`; | 		const sw = `/sw.${_VERSION_}.${_LANG_}.js`; | ||||||
| 
 | 
 | ||||||
| 		// Register service worker
 | 		// Register service worker
 | ||||||
| 		navigator.serviceWorker.register(sw).then(registration => { | 		navigator.serviceWorker.register(sw).then(registration => { | ||||||
|  | @ -310,3 +316,22 @@ export default class MiOS extends EventEmitter { | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Convert the URL safe base64 string to a Uint8Array | ||||||
|  |  * @param base64String base64 string | ||||||
|  |  */ | ||||||
|  | function urlBase64ToUint8Array(base64String: string): Uint8Array { | ||||||
|  | 	const padding = '='.repeat((4 - base64String.length % 4) % 4); | ||||||
|  | 	const base64 = (base64String + padding) | ||||||
|  | 		.replace(/\-/g, '+') | ||||||
|  | 		.replace(/_/g, '/'); | ||||||
|  | 
 | ||||||
|  | 	const rawData = window.atob(base64); | ||||||
|  | 	const outputArray = new Uint8Array(rawData.length); | ||||||
|  | 
 | ||||||
|  | 	for (let i = 0; i < rawData.length; ++i) { | ||||||
|  | 		outputArray[i] = rawData.charCodeAt(i); | ||||||
|  | 	} | ||||||
|  | 	return outputArray; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
|  * API Request |  * API Request | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| import CONFIG from './config'; | declare const _API_URL_: string; | ||||||
| 
 | 
 | ||||||
| let spinner = null; | let spinner = null; | ||||||
| let pending = 0; | let pending = 0; | ||||||
|  | @ -26,7 +26,7 @@ export default (i, endpoint, data = {}): Promise<{ [x: string]: any }> => { | ||||||
| 
 | 
 | ||||||
| 	return new Promise((resolve, reject) => { | 	return new Promise((resolve, reject) => { | ||||||
| 		// Send request
 | 		// Send request
 | ||||||
| 		fetch(endpoint.indexOf('://') > -1 ? endpoint : `${CONFIG.apiUrl}/${endpoint}`, { | 		fetch(endpoint.indexOf('://') > -1 ? endpoint : `${_API_URL_}/${endpoint}`, { | ||||||
| 			method: 'POST', | 			method: 'POST', | ||||||
| 			body: JSON.stringify(data), | 			body: JSON.stringify(data), | ||||||
| 			credentials: endpoint === 'signin' ? 'include' : 'omit' | 			credentials: endpoint === 'signin' ? 'include' : 'omit' | ||||||
|  |  | ||||||
|  | @ -1,12 +1,12 @@ | ||||||
| import MiOS from '../mios'; | import MiOS from '../mios'; | ||||||
| 
 | 
 | ||||||
| declare var VERSION: string; | declare const _VERSION_: string; | ||||||
| 
 | 
 | ||||||
| export default async function(mios: MiOS) { | export default async function(mios: MiOS) { | ||||||
| 	const meta = await mios.getMeta(); | 	const meta = await mios.getMeta(); | ||||||
| 
 | 
 | ||||||
| 	if (meta.version != VERSION) { | 	if (meta.version != _VERSION_) { | ||||||
| 		localStorage.setItem('should-refresh', 'true'); | 		localStorage.setItem('should-refresh', 'true'); | ||||||
| 		alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', VERSION)); | 		alert('%i18n:common.update-available%'.replace('{newer}', meta.version).replace('{current}', _VERSION_)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,27 +0,0 @@ | ||||||
| const _url = new URL(location.href); |  | ||||||
| 
 |  | ||||||
| const isRoot = _url.host == 'localhost' |  | ||||||
| 	? true |  | ||||||
| 	: _url.host.split('.')[0] == 'misskey'; |  | ||||||
| 
 |  | ||||||
| const host = isRoot ? _url.host : _url.host.substring(_url.host.indexOf('.') + 1, _url.host.length); |  | ||||||
| const scheme = _url.protocol; |  | ||||||
| const url = `${scheme}//${host}`; |  | ||||||
| const apiUrl = `${scheme}//api.${host}`; |  | ||||||
| const chUrl = `${scheme}//ch.${host}`; |  | ||||||
| const devUrl = `${scheme}//dev.${host}`; |  | ||||||
| const aboutUrl = `${scheme}//about.${host}`; |  | ||||||
| const statsUrl = `${scheme}//stats.${host}`; |  | ||||||
| const statusUrl = `${scheme}//status.${host}`; |  | ||||||
| 
 |  | ||||||
| export default { |  | ||||||
| 	host, |  | ||||||
| 	scheme, |  | ||||||
| 	url, |  | ||||||
| 	apiUrl, |  | ||||||
| 	chUrl, |  | ||||||
| 	devUrl, |  | ||||||
| 	aboutUrl, |  | ||||||
| 	statsUrl, |  | ||||||
| 	statusUrl |  | ||||||
| }; |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import CONFIG from './config'; | declare const _HOST_: string; | ||||||
| 
 | 
 | ||||||
| export default () => { | export default () => { | ||||||
| 	localStorage.removeItem('me'); | 	localStorage.removeItem('me'); | ||||||
| 	document.cookie = `i=; domain=.${CONFIG.host}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; | 	document.cookie = `i=; domain=.${_HOST_}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; | ||||||
| 	location.href = '/'; | 	location.href = '/'; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
|  | declare const _API_URL_: string; | ||||||
|  | 
 | ||||||
| import { EventEmitter } from 'eventemitter3'; | import { EventEmitter } from 'eventemitter3'; | ||||||
| import * as ReconnectingWebsocket from 'reconnecting-websocket'; | import * as ReconnectingWebsocket from 'reconnecting-websocket'; | ||||||
| import CONFIG from '../config'; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Misskey stream connection |  * Misskey stream connection | ||||||
|  | @ -24,7 +25,7 @@ export default class Connection extends EventEmitter { | ||||||
| 		this.state = 'initializing'; | 		this.state = 'initializing'; | ||||||
| 		this.buffer = []; | 		this.buffer = []; | ||||||
| 
 | 
 | ||||||
| 		const host = CONFIG.apiUrl.replace('http', 'ws'); | 		const host = _API_URL_.replace('http', 'ws'); | ||||||
| 		const query = params | 		const query = params | ||||||
| 			? Object.keys(params) | 			? Object.keys(params) | ||||||
| 				.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) | 				.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
|  | declare const _URL_: string; | ||||||
|  | 
 | ||||||
| import * as riot from 'riot'; | import * as riot from 'riot'; | ||||||
| import * as pictograph from 'pictograph'; | import * as pictograph from 'pictograph'; | ||||||
| import CONFIG from './config'; |  | ||||||
| 
 | 
 | ||||||
| const escape = text => | const escape = text => | ||||||
| 	text | 	text | ||||||
|  | @ -26,7 +27,7 @@ export default (tokens, shouldBreak) => { | ||||||
| 			case 'link': | 			case 'link': | ||||||
| 				return `<a class="link" href="${escape(token.url)}" target="_blank" title="${escape(token.url)}">${escape(token.title)}</a>`; | 				return `<a class="link" href="${escape(token.url)}" target="_blank" title="${escape(token.url)}">${escape(token.title)}</a>`; | ||||||
| 			case 'mention': | 			case 'mention': | ||||||
| 				return `<a href="${CONFIG.url + '/' + escape(token.username)}" target="_blank" data-user-preview="${token.content}" ${me && me.username == token.username ? 'data-is-me' : ''}>${token.content}</a>`; | 				return `<a href="${_URL_ + '/' + escape(token.username)}" target="_blank" data-user-preview="${token.content}" ${me && me.username == token.username ? 'data-is-me' : ''}>${token.content}</a>`; | ||||||
| 			case 'hashtag': // TODO
 | 			case 'hashtag': // TODO
 | ||||||
| 				return `<a>${escape(token.content)}</a>`; | 				return `<a>${escape(token.content)}</a>`; | ||||||
| 			case 'code': | 			case 'code': | ||||||
|  |  | ||||||
|  | @ -170,8 +170,6 @@ | ||||||
| 
 | 
 | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		import CONFIG from '../../common/scripts/config'; |  | ||||||
| 
 |  | ||||||
| 		this.on('mount', () => { | 		this.on('mount', () => { | ||||||
| 			this.update({ | 			this.update({ | ||||||
| 				network: navigator.onLine | 				network: navigator.onLine | ||||||
|  | @ -193,7 +191,7 @@ | ||||||
| 				}); | 				}); | ||||||
| 
 | 
 | ||||||
| 				// Check misskey server is available | 				// Check misskey server is available | ||||||
| 				fetch(`${CONFIG.apiUrl}/meta`).then(() => { | 				fetch(`${_API_URL_}/meta`).then(() => { | ||||||
| 					this.update({ | 					this.update({ | ||||||
| 						end: true, | 						end: true, | ||||||
| 						server: true | 						server: true | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 		<h1>Misskeyとは?</h1> | 		<h1>Misskeyとは?</h1> | ||||||
| 		<p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p> | 		<p><ruby>Misskey<rt>みすきー</rt></ruby>は、<a href="http://syuilo.com" target="_blank">syuilo</a>が2014年くらいから<a href="https://github.com/syuilo/misskey" target="_blank">オープンソースで</a>開発・運営を行っている、ミニブログベースのSNSです。</p> | ||||||
| 		<p>無料で誰でも利用でき、広告も掲載していません。</p> | 		<p>無料で誰でも利用でき、広告も掲載していません。</p> | ||||||
| 		<p><a href={ CONFIG.aboutUrl } target="_blank">もっと知りたい方はこちら</a></p> | 		<p><a href={ _ABOUT_URL_ } target="_blank">もっと知りたい方はこちら</a></p> | ||||||
| 	</article> | 	</article> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <mk-nav-links> | <mk-nav-links> | ||||||
| 	<a href={ CONFIG.aboutUrl }>%i18n:common.tags.mk-nav-links.about%</a><i>・</i><a href={ CONFIG.statsUrl }>%i18n:common.tags.mk-nav-links.stats%</a><i>・</i><a href={ CONFIG.statusUrl }>%i18n:common.tags.mk-nav-links.status%</a><i>・</i><a href="http://zawazawa.jp/misskey/">%i18n:common.tags.mk-nav-links.wiki%</a><i>・</i><a href="https://github.com/syuilo/misskey/blob/master/DONORS.md">%i18n:common.tags.mk-nav-links.donors%</a><i>・</i><a href="https://github.com/syuilo/misskey">%i18n:common.tags.mk-nav-links.repository%</a><i>・</i><a href={ CONFIG.devUrl }>%i18n:common.tags.mk-nav-links.develop%</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on <i class="fa fa-twitter"></i></a> | 	<a href={ _ABOUT_URL_ }>%i18n:common.tags.mk-nav-links.about%</a><i>・</i><a href={ _STATS_URL_ }>%i18n:common.tags.mk-nav-links.stats%</a><i>・</i><a href={ _STATUS_URL_ }>%i18n:common.tags.mk-nav-links.status%</a><i>・</i><a href="http://zawazawa.jp/misskey/">%i18n:common.tags.mk-nav-links.wiki%</a><i>・</i><a href="https://github.com/syuilo/misskey/blob/master/DONORS.md">%i18n:common.tags.mk-nav-links.donors%</a><i>・</i><a href="https://github.com/syuilo/misskey">%i18n:common.tags.mk-nav-links.repository%</a><i>・</i><a href={ _DEV_URL_ }>%i18n:common.tags.mk-nav-links.develop%</a><i>・</i><a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on <i class="fa fa-twitter"></i></a> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
| 			display inline | 			display inline | ||||||
|  |  | ||||||
|  | @ -3,7 +3,7 @@ | ||||||
| 		<label class="username"> | 		<label class="username"> | ||||||
| 			<p class="caption"><i class="fa fa-at"></i>%i18n:common.tags.mk-signup.username%</p> | 			<p class="caption"><i class="fa fa-at"></i>%i18n:common.tags.mk-signup.username%</p> | ||||||
| 			<input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup={ onChangeUsername }/> | 			<input ref="username" type="text" pattern="^[a-zA-Z0-9-]{3,20}$" placeholder="a~z、A~Z、0~9、-" autocomplete="off" required="required" onkeyup={ onChangeUsername }/> | ||||||
| 			<p class="profile-page-url-preview" if={ refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange' }>{ CONFIG.url + '/' + refs.username.value }</p> | 			<p class="profile-page-url-preview" if={ refs.username.value != '' && username-state != 'invalidFormat' && username-state != 'minRange' && username-state != 'maxRange' }>{ _URL_ + '/' + refs.username.value }</p> | ||||||
| 			<p class="info" if={ usernameState == 'wait' } style="color:#999"><i class="fa fa-fw fa-spinner fa-pulse"></i>%i18n:common.tags.mk-signup.checking%</p> | 			<p class="info" if={ usernameState == 'wait' } style="color:#999"><i class="fa fa-fw fa-spinner fa-pulse"></i>%i18n:common.tags.mk-signup.checking%</p> | ||||||
| 			<p class="info" if={ usernameState == 'ok' } style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>%i18n:common.tags.mk-signup.available%</p> | 			<p class="info" if={ usernameState == 'ok' } style="color:#3CB7B5"><i class="fa fa-fw fa-check"></i>%i18n:common.tags.mk-signup.available%</p> | ||||||
| 			<p class="info" if={ usernameState == 'unavailable' } style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>%i18n:common.tags.mk-signup.unavailable%</p> | 			<p class="info" if={ usernameState == 'unavailable' } style="color:#FF1161"><i class="fa fa-fw fa-exclamation-triangle"></i>%i18n:common.tags.mk-signup.unavailable%</p> | ||||||
|  | @ -30,7 +30,7 @@ | ||||||
| 		</label> | 		</label> | ||||||
| 		<label class="recaptcha"> | 		<label class="recaptcha"> | ||||||
| 			<p class="caption"><i class="fa fa-toggle-on" if={ recaptchaed }></i><i class="fa fa-toggle-off" if={ !recaptchaed }></i>%i18n:common.tags.mk-signup.recaptcha%</p> | 			<p class="caption"><i class="fa fa-toggle-on" if={ recaptchaed }></i><i class="fa fa-toggle-off" if={ !recaptchaed }></i>%i18n:common.tags.mk-signup.recaptcha%</p> | ||||||
| 			<div if={ recaptcha } class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.siteKey }></div> | 			<div if={ recaptcha } class="g-recaptcha" data-callback="onRecaptchaed" data-expired-callback="onRecaptchaExpired" data-sitekey={ recaptcha.site_key }></div> | ||||||
| 		</label> | 		</label> | ||||||
| 		<label class="agree-tou"> | 		<label class="agree-tou"> | ||||||
| 			<input name="agree-tou" type="checkbox" autocomplete="off" required="required"/> | 			<input name="agree-tou" type="checkbox" autocomplete="off" required="required"/> | ||||||
|  | @ -193,11 +193,9 @@ | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 		this.on('mount', () => { | 		this.on('mount', () => { | ||||||
| 			fetch('/config.json').then(res => { |  | ||||||
| 				res.json().then(conf => { |  | ||||||
| 			this.update({ | 			this.update({ | ||||||
| 				recaptcha: { | 				recaptcha: { | ||||||
| 							siteKey: conf.recaptcha.siteKey | 					site_key: _RECAPTCHA_SITEKEY_ | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
|  | @ -206,8 +204,6 @@ | ||||||
| 			script.setAttribute('src', 'https://www.google.com/recaptcha/api.js'); | 			script.setAttribute('src', 'https://www.google.com/recaptcha/api.js'); | ||||||
| 			head.appendChild(script); | 			head.appendChild(script); | ||||||
| 		}); | 		}); | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
| 
 | 
 | ||||||
| 		this.onChangeUsername = () => { | 		this.onChangeUsername = () => { | ||||||
| 			const username = this.refs.username.value; | 			const username = this.refs.username.value; | ||||||
|  |  | ||||||
|  | @ -1,10 +1,10 @@ | ||||||
| <mk-twitter-setting> | <mk-twitter-setting> | ||||||
| 	<p>%i18n:common.tags.mk-twitter-setting.description%<a href={ CONFIG.aboutUrl + '/link-to-twitter' } target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p> | 	<p>%i18n:common.tags.mk-twitter-setting.description%<a href={ _ABOUT_URL_ + '/link-to-twitter' } target="_blank">%i18n:common.tags.mk-twitter-setting.detail%</a></p> | ||||||
| 	<p class="account" if={ I.twitter } title={ 'Twitter ID: ' + I.twitter.user_id }>%i18n:common.tags.mk-twitter-setting.connected-to%: <a href={ 'https://twitter.com/' + I.twitter.screen_name } target="_blank">@{ I.twitter.screen_name }</a></p> | 	<p class="account" if={ I.twitter } title={ 'Twitter ID: ' + I.twitter.user_id }>%i18n:common.tags.mk-twitter-setting.connected-to%: <a href={ 'https://twitter.com/' + I.twitter.screen_name } target="_blank">@{ I.twitter.screen_name }</a></p> | ||||||
| 	<p> | 	<p> | ||||||
| 		<a href={ CONFIG.apiUrl + '/connect/twitter' } target="_blank" onclick={ connect }>{ I.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }</a> | 		<a href={ _API_URL_ + '/connect/twitter' } target="_blank" onclick={ connect }>{ I.twitter ? '%i18n:common.tags.mk-twitter-setting.reconnect%' : '%i18n:common.tags.mk-twitter-setting.connect%' }</a> | ||||||
| 		<span if={ I.twitter }> or </span> | 		<span if={ I.twitter }> or </span> | ||||||
| 		<a href={ CONFIG.apiUrl + '/disconnect/twitter' } target="_blank" if={ I.twitter } onclick={ disconnect }>%i18n:common.tags.mk-twitter-setting.disconnect%</a> | 		<a href={ _API_URL_ + '/disconnect/twitter' } target="_blank" if={ I.twitter } onclick={ disconnect }>%i18n:common.tags.mk-twitter-setting.disconnect%</a> | ||||||
| 	</p> | 	</p> | ||||||
| 	<p class="id" if={ I.twitter }>Twitter ID: { I.twitter.user_id }</p> | 	<p class="id" if={ I.twitter }>Twitter ID: { I.twitter.user_id }</p> | ||||||
| 	<style> | 	<style> | ||||||
|  | @ -25,8 +25,6 @@ | ||||||
| 				color #8899a6 | 				color #8899a6 | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		import CONFIG from '../scripts/config'; |  | ||||||
| 
 |  | ||||||
| 		this.mixin('i'); | 		this.mixin('i'); | ||||||
| 
 | 
 | ||||||
| 		this.form = null; | 		this.form = null; | ||||||
|  | @ -47,7 +45,7 @@ | ||||||
| 
 | 
 | ||||||
| 		this.connect = e => { | 		this.connect = e => { | ||||||
| 			e.preventDefault(); | 			e.preventDefault(); | ||||||
| 			this.form = window.open(CONFIG.apiUrl + '/connect/twitter', | 			this.form = window.open(_API_URL_ + '/connect/twitter', | ||||||
| 				'twitter_connect_window', | 				'twitter_connect_window', | ||||||
| 				'height=570,width=520'); | 				'height=570,width=520'); | ||||||
| 			return false; | 			return false; | ||||||
|  | @ -55,7 +53,7 @@ | ||||||
| 
 | 
 | ||||||
| 		this.disconnect = e => { | 		this.disconnect = e => { | ||||||
| 			e.preventDefault(); | 			e.preventDefault(); | ||||||
| 			window.open(CONFIG.apiUrl + '/disconnect/twitter', | 			window.open(_API_URL_ + '/disconnect/twitter', | ||||||
| 				'twitter_disconnect_window', | 				'twitter_disconnect_window', | ||||||
| 				'height=570,width=520'); | 				'height=570,width=520'); | ||||||
| 			return false; | 			return false; | ||||||
|  |  | ||||||
|  | @ -172,7 +172,7 @@ | ||||||
| 			if (folder) data.append('folder_id', folder); | 			if (folder) data.append('folder_id', folder); | ||||||
| 
 | 
 | ||||||
| 			const xhr = new XMLHttpRequest(); | 			const xhr = new XMLHttpRequest(); | ||||||
| 			xhr.open('POST', this.CONFIG.apiUrl + '/drive/files/create', true); | 			xhr.open('POST', _API_URL_ + '/drive/files/create', true); | ||||||
| 			xhr.onload = e => { | 			xhr.onload = e => { | ||||||
| 				const driveFile = JSON.parse(e.target.response); | 				const driveFile = JSON.parse(e.target.response); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| require('fuckadblock'); | require('fuckadblock'); | ||||||
| import dialog from './dialog'; | import dialog from './dialog'; | ||||||
| 
 | 
 | ||||||
| declare var fuckAdBlock: any; | declare const fuckAdBlock: any; | ||||||
| 
 | 
 | ||||||
| export default () => { | export default () => { | ||||||
| 	if (fuckAdBlock === undefined) { | 	if (fuckAdBlock === undefined) { | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
|  | declare const _API_URL_: string; | ||||||
|  | 
 | ||||||
| import * as riot from 'riot'; | import * as riot from 'riot'; | ||||||
| import CONFIG from '../../common/scripts/config'; |  | ||||||
| import dialog from './dialog'; | import dialog from './dialog'; | ||||||
| import api from '../../common/scripts/api'; | import api from '../../common/scripts/api'; | ||||||
| 
 | 
 | ||||||
|  | @ -44,7 +45,7 @@ export default (I, cb, file = null) => { | ||||||
| 		if (folder) data.append('folder_id', folder.id); | 		if (folder) data.append('folder_id', folder.id); | ||||||
| 
 | 
 | ||||||
| 		const xhr = new XMLHttpRequest(); | 		const xhr = new XMLHttpRequest(); | ||||||
| 		xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); | 		xhr.open('POST', _API_URL_ + '/drive/files/create', true); | ||||||
| 		xhr.onload = e => { | 		xhr.onload = e => { | ||||||
| 			const file = JSON.parse((e.target as any).response); | 			const file = JSON.parse((e.target as any).response); | ||||||
| 			progress.close(); | 			progress.close(); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
|  | declare const _API_URL_: string; | ||||||
|  | 
 | ||||||
| import * as riot from 'riot'; | import * as riot from 'riot'; | ||||||
| import CONFIG from '../../common/scripts/config'; |  | ||||||
| import dialog from './dialog'; | import dialog from './dialog'; | ||||||
| import api from '../../common/scripts/api'; | import api from '../../common/scripts/api'; | ||||||
| 
 | 
 | ||||||
|  | @ -44,7 +45,7 @@ export default (I, cb, file = null) => { | ||||||
| 		if (folder) data.append('folder_id', folder.id); | 		if (folder) data.append('folder_id', folder.id); | ||||||
| 
 | 
 | ||||||
| 		const xhr = new XMLHttpRequest(); | 		const xhr = new XMLHttpRequest(); | ||||||
| 		xhr.open('POST', CONFIG.apiUrl + '/drive/files/create', true); | 		xhr.open('POST', _API_URL_ + '/drive/files/create', true); | ||||||
| 		xhr.onload = e => { | 		xhr.onload = e => { | ||||||
| 			const file = JSON.parse((e.target as any).response); | 			const file = JSON.parse((e.target as any).response); | ||||||
| 			progress.close(); | 			progress.close(); | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ | ||||||
| 				const length = Math.min(canvW, canvH) / 4; | 				const length = Math.min(canvW, canvH) / 4; | ||||||
| 				const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); | 				const uv = new Vec2(Math.sin(angle), -Math.cos(angle)); | ||||||
| 				ctx.beginPath(); | 				ctx.beginPath(); | ||||||
| 				ctx.strokeStyle = THEME_COLOR; | 				ctx.strokeStyle = _THEME_COLOR_; | ||||||
| 				ctx.lineWidth = 2; | 				ctx.lineWidth = 2; | ||||||
| 				ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5); | 				ctx.moveTo(canvW / 2 - uv.x * length / 5, canvH / 2 - uv.y * length / 5); | ||||||
| 				ctx.lineTo(canvW / 2 + uv.x * length,     canvH / 2 + uv.y * length); | 				ctx.lineTo(canvW / 2 + uv.x * length,     canvH / 2 + uv.y * length); | ||||||
|  |  | ||||||
|  | @ -28,8 +28,6 @@ | ||||||
| 
 | 
 | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		import CONFIG from '../../../common/scripts/config'; |  | ||||||
| 
 |  | ||||||
| 		this.mixin('api'); | 		this.mixin('api'); | ||||||
| 
 | 
 | ||||||
| 		this.folder = this.opts.folder ? this.opts.folder : null; | 		this.folder = this.opts.folder ? this.opts.folder : null; | ||||||
|  | @ -37,9 +35,9 @@ | ||||||
| 		this.popout = () => { | 		this.popout = () => { | ||||||
| 			const folder = this.refs.window.refs.browser.folder; | 			const folder = this.refs.window.refs.browser.folder; | ||||||
| 			if (folder) { | 			if (folder) { | ||||||
| 				return `${CONFIG.url}/i/drive/folder/${folder.id}`; | 				return `${_URL_}/i/drive/folder/${folder.id}`; | ||||||
| 			} else { | 			} else { | ||||||
| 				return `${CONFIG.url}/i/drive`; | 				return `${_URL_}/i/drive`; | ||||||
| 			} | 			} | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -114,8 +114,8 @@ | ||||||
| 				let broadcasts = []; | 				let broadcasts = []; | ||||||
| 				if (meta.broadcasts) { | 				if (meta.broadcasts) { | ||||||
| 					meta.broadcasts.forEach(broadcast => { | 					meta.broadcasts.forEach(broadcast => { | ||||||
| 						if (broadcast[LANG]) { | 						if (broadcast[_LANG_]) { | ||||||
| 							broadcasts.push(broadcast[LANG]); | 							broadcasts.push(broadcast[_LANG_]); | ||||||
| 						} | 						} | ||||||
| 					}); | 					}); | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -193,7 +193,7 @@ | ||||||
| <mk-channel-post> | <mk-channel-post> | ||||||
| 	<header> | 	<header> | ||||||
| 		<a class="index" onclick={ reply }>{ post.index }:</a> | 		<a class="index" onclick={ reply }>{ post.index }:</a> | ||||||
| 		<a class="name" href={ CONFIG.url + '/' + post.user.username }><b>{ post.user.name }</b></a> | 		<a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a> | ||||||
| 		<span>ID:<i>{ post.user.username }</i></span> | 		<span>ID:<i>{ post.user.username }</i></span> | ||||||
| 	</header> | 	</header> | ||||||
| 	<div> | 	<div> | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| <mk-version-home-widget> | <mk-version-home-widget> | ||||||
| 	<p>ver { version } (葵 aoi)</p> | 	<p>ver { _VERSION_ } (葵 aoi)</p> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
| 			display block | 			display block | ||||||
|  | @ -16,7 +16,5 @@ | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		this.mixin('widget'); | 		this.mixin('widget'); | ||||||
| 
 |  | ||||||
| 		this.version = VERSION; |  | ||||||
| 	</script> | 	</script> | ||||||
| </mk-version-home-widget> | </mk-version-home-widget> | ||||||
|  |  | ||||||
|  | @ -19,11 +19,9 @@ | ||||||
| 
 | 
 | ||||||
| 	</style> | 	</style> | ||||||
| 	<script> | 	<script> | ||||||
| 		import CONFIG from '../../../common/scripts/config'; |  | ||||||
| 
 |  | ||||||
| 		this.user = this.opts.user; | 		this.user = this.opts.user; | ||||||
| 
 | 
 | ||||||
| 		this.popout = `${CONFIG.url}/i/messaging/${this.user.username}`; | 		this.popout = `${_URL_}/i/messaging/${this.user.username}`; | ||||||
| 
 | 
 | ||||||
| 		this.on('mount', () => { | 		this.on('mount', () => { | ||||||
| 			this.refs.window.on('closed', () => { | 			this.refs.window.on('closed', () => { | ||||||
|  |  | ||||||
|  | @ -150,7 +150,7 @@ | ||||||
| </mk-entrance> | </mk-entrance> | ||||||
| 
 | 
 | ||||||
| <mk-entrance-signin> | <mk-entrance-signin> | ||||||
| 	<a class="help" href={ CONFIG.aboutUrl + '/help' } title="お困りですか?"><i class="fa fa-question"></i></a> | 	<a class="help" href={ _ABOUT_URL_ + '/help' } title="お困りですか?"><i class="fa fa-question"></i></a> | ||||||
| 	<div class="form"> | 	<div class="form"> | ||||||
| 		<h1><img if={ user } src={ user.avatar_url + '?thumbnail&size=32' }/> | 		<h1><img if={ user } src={ user.avatar_url + '?thumbnail&size=32' }/> | ||||||
| 			<p>{ user ? user.name : 'アカウント' }</p> | 			<p>{ user ? user.name : 'アカウント' }</p> | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ | ||||||
| 			</header> | 			</header> | ||||||
| 			<div class="body"> | 			<div class="body"> | ||||||
| 				<div class="text" ref="text"> | 				<div class="text" ref="text"> | ||||||
| 					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> | 					<p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> | ||||||
| 					<a class="reply" if={ p.reply }> | 					<a class="reply" if={ p.reply }> | ||||||
| 						<i class="fa fa-reply"></i> | 						<i class="fa fa-reply"></i> | ||||||
| 					</a> | 					</a> | ||||||
|  |  | ||||||
|  | @ -379,7 +379,7 @@ | ||||||
| 	<ul> | 	<ul> | ||||||
| 		<virtual if={ SIGNIN }> | 		<virtual if={ SIGNIN }> | ||||||
| 			<li class="home { active: page == 'home' }"> | 			<li class="home { active: page == 'home' }"> | ||||||
| 				<a href={ CONFIG.url }> | 				<a href={ _URL_ }> | ||||||
| 					<i class="fa fa-home"></i> | 					<i class="fa fa-home"></i> | ||||||
| 					<p>%i18n:desktop.tags.mk-ui-header-nav.home%</p> | 					<p>%i18n:desktop.tags.mk-ui-header-nav.home%</p> | ||||||
| 				</a> | 				</a> | ||||||
|  | @ -393,7 +393,7 @@ | ||||||
| 			</li> | 			</li> | ||||||
| 		</virtual> | 		</virtual> | ||||||
| 		<li class="ch"> | 		<li class="ch"> | ||||||
| 			<a href={ CONFIG.chUrl } target="_blank"> | 			<a href={ _CH_URL_ } target="_blank"> | ||||||
| 				<i class="fa fa-television"></i> | 				<i class="fa fa-television"></i> | ||||||
| 				<p>%i18n:desktop.tags.mk-ui-header-nav.ch%</p> | 				<p>%i18n:desktop.tags.mk-ui-header-nav.ch%</p> | ||||||
| 			</a> | 			</a> | ||||||
|  |  | ||||||
|  | @ -2,13 +2,12 @@ | ||||||
|  * App initializer |  * App initializer | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| declare var VERSION: string; | declare const _VERSION_: string; | ||||||
| declare var LANG: string; | declare const _LANG_: string; | ||||||
|  | declare const _HOST_: string; | ||||||
| 
 | 
 | ||||||
| import * as riot from 'riot'; |  | ||||||
| import checkForUpdate from './common/scripts/check-for-update'; | import checkForUpdate from './common/scripts/check-for-update'; | ||||||
| import mixin from './common/mixins'; | import mixin from './common/mixins'; | ||||||
| import CONFIG from './common/scripts/config'; |  | ||||||
| import MiOS from './common/mios'; | import MiOS from './common/mios'; | ||||||
| require('./common/tags'); | require('./common/tags'); | ||||||
| 
 | 
 | ||||||
|  | @ -16,15 +15,15 @@ require('./common/tags'); | ||||||
|  * APP ENTRY POINT! |  * APP ENTRY POINT! | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| console.info(`Misskey v${VERSION} (葵 aoi)`); | console.info(`Misskey v${_VERSION_} (葵 aoi)`); | ||||||
| 
 | 
 | ||||||
| if (CONFIG.host != 'localhost') { | if (_HOST_ != 'localhost') { | ||||||
| 	document.domain = CONFIG.host; | 	document.domain = _HOST_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| { // Set lang attr
 | { // Set lang attr
 | ||||||
| 	const html = document.documentElement; | 	const html = document.documentElement; | ||||||
| 	html.setAttribute('lang', LANG); | 	html.setAttribute('lang', _LANG_); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| { // Set description meta tag
 | { // Set description meta tag
 | ||||||
|  | @ -35,9 +34,6 @@ if (CONFIG.host != 'localhost') { | ||||||
| 	head.appendChild(meta); | 	head.appendChild(meta); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Set global configuration
 |  | ||||||
| (riot as any).mixin({ CONFIG }); |  | ||||||
| 
 |  | ||||||
| // iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
 | // iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
 | ||||||
| try { | try { | ||||||
| 	localStorage.setItem('kyoppie', 'yuppie'); | 	localStorage.setItem('kyoppie', 'yuppie'); | ||||||
|  | @ -94,7 +90,7 @@ function panic(e) { | ||||||
| 			+ '<hr>' | 			+ '<hr>' | ||||||
| 			+ `<p>エラーコード: ${e.toString()}</p>` | 			+ `<p>エラーコード: ${e.toString()}</p>` | ||||||
| 			+ `<p>ブラウザ バージョン: ${navigator.userAgent}</p>` | 			+ `<p>ブラウザ バージョン: ${navigator.userAgent}</p>` | ||||||
| 			+ `<p>クライアント バージョン: ${VERSION}</p>` | 			+ `<p>クライアント バージョン: ${_VERSION_}</p>` | ||||||
| 			+ '<hr>' | 			+ '<hr>' | ||||||
| 			+ '<p>問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。</p>' | 			+ '<p>問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。</p>' | ||||||
| 			+ '<p>Thank you for using Misskey.</p>' | 			+ '<p>Thank you for using Misskey.</p>' | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ | ||||||
| 	<ul> | 	<ul> | ||||||
| 		<li><a onclick={ signout }><i class="fa fa-power-off"></i>%i18n:mobile.tags.mk-settings-page.signout%</a></li> | 		<li><a onclick={ signout }><i class="fa fa-power-off"></i>%i18n:mobile.tags.mk-settings-page.signout%</a></li> | ||||||
| 	</ul> | 	</ul> | ||||||
| 	<p><small>ver { version } (葵 aoi)</small></p> | 	<p><small>ver { _VERSION_ } (葵 aoi)</small></p> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
| 			display block | 			display block | ||||||
|  | @ -97,7 +97,5 @@ | ||||||
| 		this.signout = signout; | 		this.signout = signout; | ||||||
| 
 | 
 | ||||||
| 		this.mixin('i'); | 		this.mixin('i'); | ||||||
| 
 |  | ||||||
| 		this.version = VERSION; |  | ||||||
| 	</script> | 	</script> | ||||||
| </mk-settings> | </mk-settings> | ||||||
|  |  | ||||||
|  | @ -164,7 +164,7 @@ | ||||||
| 			</header> | 			</header> | ||||||
| 			<div class="body"> | 			<div class="body"> | ||||||
| 				<div class="text" ref="text"> | 				<div class="text" ref="text"> | ||||||
| 					<p class="channel" if={ p.channel != null }><a href={ CONFIG.chUrl + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> | 					<p class="channel" if={ p.channel != null }><a href={ _CH_URL_ + '/' + p.channel.id } target="_blank">{ p.channel.title }</a>:</p> | ||||||
| 					<a class="reply" if={ p.reply }> | 					<a class="reply" if={ p.reply }> | ||||||
| 						<i class="fa fa-reply"></i> | 						<i class="fa fa-reply"></i> | ||||||
| 					</a> | 					</a> | ||||||
|  |  | ||||||
|  | @ -239,7 +239,7 @@ | ||||||
| 				<li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li> | 				<li><a href="/i/messaging"><i class="fa fa-comments-o"></i>%i18n:mobile.tags.mk-ui-nav.messaging%<i class="i fa fa-circle" if={ hasUnreadMessagingMessages }></i><i class="fa fa-angle-right"></i></a></li> | ||||||
| 			</ul> | 			</ul> | ||||||
| 			<ul> | 			<ul> | ||||||
| 				<li><a href={ CONFIG.chUrl } target="_blank"><i class="fa fa-television"></i>%i18n:mobile.tags.mk-ui-nav.ch%<i class="fa fa-angle-right"></i></a></li> | 				<li><a href={ _CH_URL_ } target="_blank"><i class="fa fa-television"></i>%i18n:mobile.tags.mk-ui-nav.ch%<i class="fa fa-angle-right"></i></a></li> | ||||||
| 				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li> | 				<li><a href="/i/drive"><i class="fa fa-cloud"></i>%i18n:mobile.tags.mk-ui-nav.drive%<i class="fa fa-angle-right"></i></a></li> | ||||||
| 			</ul> | 			</ul> | ||||||
| 			<ul> | 			<ul> | ||||||
|  | @ -249,7 +249,7 @@ | ||||||
| 				<li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li> | 				<li><a href="/i/settings"><i class="fa fa-cog"></i>%i18n:mobile.tags.mk-ui-nav.settings%<i class="fa fa-angle-right"></i></a></li> | ||||||
| 			</ul> | 			</ul> | ||||||
| 		</div> | 		</div> | ||||||
| 		<a href={ CONFIG.aboutUrl }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a> | 		<a href={ _ABOUT_URL_ }><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a> | ||||||
| 	</div> | 	</div> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ | ||||||
| 		<mk-users stats={ stats }/> | 		<mk-users stats={ stats }/> | ||||||
| 		<mk-posts stats={ stats }/> | 		<mk-posts stats={ stats }/> | ||||||
| 	</main> | 	</main> | ||||||
| 	<footer><a href={ CONFIG.url }>{ CONFIG.host }</a></footer> | 	<footer><a href={ _URL_ }>{ _HOST_ }</a></footer> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
| 			display block | 			display block | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
| 		<mk-cpu-usage connection={ connection }/> | 		<mk-cpu-usage connection={ connection }/> | ||||||
| 		<mk-mem-usage connection={ connection }/> | 		<mk-mem-usage connection={ connection }/> | ||||||
| 	</main> | 	</main> | ||||||
| 	<footer><a href={ CONFIG.url }>{ CONFIG.host }</a></footer> | 	<footer><a href={ _URL_ }>{ _HOST_ }</a></footer> | ||||||
| 	<style> | 	<style> | ||||||
| 		:scope | 		:scope | ||||||
| 			display block | 			display block | ||||||
|  |  | ||||||
|  | @ -11,8 +11,6 @@ import * as bodyParser from 'body-parser'; | ||||||
| import * as favicon from 'serve-favicon'; | import * as favicon from 'serve-favicon'; | ||||||
| import * as compression from 'compression'; | import * as compression from 'compression'; | ||||||
| 
 | 
 | ||||||
| import config from '../conf'; |  | ||||||
| 
 |  | ||||||
| /** | /** | ||||||
|  * Init app |  * Init app | ||||||
|  */ |  */ | ||||||
|  | @ -50,29 +48,7 @@ app.get(/^\/sw\.(.+?)\.js$/, (req, res) => res.sendFile(`${__dirname}/assets/sw. | ||||||
| /** | /** | ||||||
|  * Manifest |  * Manifest | ||||||
|  */ |  */ | ||||||
| app.get('/manifest.json', (req, res) => { | app.get('/manifest.json', (req, res) => res.sendFile(`${__dirname}/assets/manifest.json`)); | ||||||
| 	const manifest = require((`${__dirname}/assets/manifest.json`)); |  | ||||||
| 
 |  | ||||||
| 	// Service Worker
 |  | ||||||
| 	if (config.sw) { |  | ||||||
| 		manifest['gcm_sender_id'] = config.sw.gcm_sender_id; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	res.send(manifest); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Serve config |  | ||||||
|  */ |  | ||||||
| app.get('/config.json', (req, res) => { |  | ||||||
| 	const conf = { |  | ||||||
| 		recaptcha: { |  | ||||||
| 			siteKey: config.recaptcha.siteKey |  | ||||||
| 		} |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	res.send(conf); |  | ||||||
| }); |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Common API |  * Common API | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								webpack/module/rules/consts.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								webpack/module/rules/consts.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | ||||||
|  | /** | ||||||
|  |  * Replace consts | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | const StringReplacePlugin = require('string-replace-webpack-plugin'); | ||||||
|  | 
 | ||||||
|  | import version from '../../../src/version'; | ||||||
|  | const constants = require('../../../src/const.json'); | ||||||
|  | import config from '../../../src/conf'; | ||||||
|  | 
 | ||||||
|  | export default lang => { | ||||||
|  | 	// 置換の誤爆を防ぐため文字数の多い順に並べてください
 | ||||||
|  | 	const consts = { | ||||||
|  | 		_RECAPTCHA_SITEKEY_: JSON.stringify(config.recaptcha.site_key), | ||||||
|  | 		_SW_PUBLICKEY_: JSON.stringify(config.sw.public_key), | ||||||
|  | 		_THEME_COLOR_: JSON.stringify(constants.themeColor), | ||||||
|  | 		_VERSION_: JSON.stringify(version), | ||||||
|  | 		_API_URL_: JSON.stringify(config.api_url), | ||||||
|  | 		_LANG_: JSON.stringify(lang), | ||||||
|  | 		_HOST_: JSON.stringify(config.host), | ||||||
|  | 		_URL_: JSON.stringify(config.url), | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	const replacements = Object.keys(consts).map(key => ({ | ||||||
|  | 		pattern: new RegExp(key, 'g'), replacement: () => consts[key] | ||||||
|  | 	})); | ||||||
|  | 
 | ||||||
|  | 	return { | ||||||
|  | 		enforce: 'post', | ||||||
|  | 		test: /\.(tag|js|ts)$/, | ||||||
|  | 		exclude: /node_modules/, | ||||||
|  | 		loader: StringReplacePlugin.replace({ | ||||||
|  | 			replacements: replacements | ||||||
|  | 		}) | ||||||
|  | 	}; | ||||||
|  | }; | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import i18n from './i18n'; | import i18n from './i18n'; | ||||||
|  | import consts from './consts'; | ||||||
| import base64 from './base64'; | import base64 from './base64'; | ||||||
| import themeColor from './theme-color'; | import themeColor from './theme-color'; | ||||||
| import tag from './tag'; | import tag from './tag'; | ||||||
|  | @ -7,6 +8,7 @@ import typescript from './typescript'; | ||||||
| 
 | 
 | ||||||
| export default (lang, locale) => [ | export default (lang, locale) => [ | ||||||
| 	i18n(lang, locale), | 	i18n(lang, locale), | ||||||
|  | 	consts(lang), | ||||||
| 	base64(), | 	base64(), | ||||||
| 	themeColor(), | 	themeColor(), | ||||||
| 	tag(), | 	tag(), | ||||||
|  |  | ||||||
|  | @ -1,14 +0,0 @@ | ||||||
| /** |  | ||||||
|  * Constant Replacer |  | ||||||
|  */ |  | ||||||
| 
 |  | ||||||
| import * as webpack from 'webpack'; |  | ||||||
| 
 |  | ||||||
| import version from '../../src/version'; |  | ||||||
| const constants = require('../../src/const.json'); |  | ||||||
| 
 |  | ||||||
| export default lang => new webpack.DefinePlugin({ |  | ||||||
| 	VERSION: JSON.stringify(version), |  | ||||||
| 	LANG: JSON.stringify(lang), |  | ||||||
| 	THEME_COLOR: JSON.stringify(constants.themeColor) |  | ||||||
| }); |  | ||||||
|  | @ -1,6 +1,5 @@ | ||||||
| const StringReplacePlugin = require('string-replace-webpack-plugin'); | const StringReplacePlugin = require('string-replace-webpack-plugin'); | ||||||
| 
 | 
 | ||||||
| import constant from './const'; |  | ||||||
| import hoist from './hoist'; | import hoist from './hoist'; | ||||||
| //import minify from './minify';
 | //import minify from './minify';
 | ||||||
| import banner from './banner'; | import banner from './banner'; | ||||||
|  | @ -10,7 +9,6 @@ const isProduction = env === 'production'; | ||||||
| 
 | 
 | ||||||
| export default (version, lang) => { | export default (version, lang) => { | ||||||
| 	const plugins = [ | 	const plugins = [ | ||||||
| 		constant(lang), |  | ||||||
| 		new StringReplacePlugin(), | 		new StringReplacePlugin(), | ||||||
| 		hoist() | 		hoist() | ||||||
| 	]; | 	]; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue