forked from Qortal/qortal
CalDescent
1 year ago
21 changed files with 860 additions and 24 deletions
@ -0,0 +1,173 @@
|
||||
package org.qortal.api; |
||||
|
||||
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource; |
||||
import org.eclipse.jetty.http.HttpVersion; |
||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler; |
||||
import org.eclipse.jetty.server.*; |
||||
import org.eclipse.jetty.server.handler.ErrorHandler; |
||||
import org.eclipse.jetty.server.handler.InetAccessHandler; |
||||
import org.eclipse.jetty.servlet.FilterHolder; |
||||
import org.eclipse.jetty.servlet.ServletContextHandler; |
||||
import org.eclipse.jetty.servlet.ServletHolder; |
||||
import org.eclipse.jetty.servlets.CrossOriginFilter; |
||||
import org.eclipse.jetty.util.ssl.SslContextFactory; |
||||
import org.glassfish.jersey.server.ResourceConfig; |
||||
import org.glassfish.jersey.servlet.ServletContainer; |
||||
import org.qortal.api.resource.AnnotationPostProcessor; |
||||
import org.qortal.api.resource.ApiDefinition; |
||||
import org.qortal.network.Network; |
||||
import org.qortal.repository.DataException; |
||||
import org.qortal.settings.Settings; |
||||
|
||||
import javax.net.ssl.KeyManagerFactory; |
||||
import javax.net.ssl.SSLContext; |
||||
import java.io.InputStream; |
||||
import java.net.InetAddress; |
||||
import java.net.InetSocketAddress; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Path; |
||||
import java.nio.file.Paths; |
||||
import java.security.KeyStore; |
||||
import java.security.SecureRandom; |
||||
|
||||
public class DevProxyService { |
||||
|
||||
private static DevProxyService instance; |
||||
|
||||
private final ResourceConfig config; |
||||
private Server server; |
||||
|
||||
private DevProxyService() { |
||||
this.config = new ResourceConfig(); |
||||
this.config.packages("org.qortal.api.proxy.resource", "org.qortal.api.resource"); |
||||
this.config.register(OpenApiResource.class); |
||||
this.config.register(ApiDefinition.class); |
||||
this.config.register(AnnotationPostProcessor.class); |
||||
} |
||||
|
||||
public static DevProxyService getInstance() { |
||||
if (instance == null) |
||||
instance = new DevProxyService(); |
||||
|
||||
return instance; |
||||
} |
||||
|
||||
public Iterable<Class<?>> getResources() { |
||||
return this.config.getClasses(); |
||||
} |
||||
|
||||
public void start() throws DataException { |
||||
try { |
||||
// Create API server
|
||||
|
||||
// SSL support if requested
|
||||
String keystorePathname = Settings.getInstance().getSslKeystorePathname(); |
||||
String keystorePassword = Settings.getInstance().getSslKeystorePassword(); |
||||
|
||||
if (keystorePathname != null && keystorePassword != null) { |
||||
// SSL version
|
||||
if (!Files.isReadable(Path.of(keystorePathname))) |
||||
throw new RuntimeException("Failed to start SSL API due to broken keystore"); |
||||
|
||||
// BouncyCastle-specific SSLContext build
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS", "BCJSSE"); |
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX", "BCJSSE"); |
||||
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType(), "BC"); |
||||
|
||||
try (InputStream keystoreStream = Files.newInputStream(Paths.get(keystorePathname))) { |
||||
keyStore.load(keystoreStream, keystorePassword.toCharArray()); |
||||
} |
||||
|
||||
keyManagerFactory.init(keyStore, keystorePassword.toCharArray()); |
||||
sslContext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()); |
||||
|
||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); |
||||
sslContextFactory.setSslContext(sslContext); |
||||
|
||||
this.server = new Server(); |
||||
|
||||
HttpConfiguration httpConfig = new HttpConfiguration(); |
||||
httpConfig.setSecureScheme("https"); |
||||
httpConfig.setSecurePort(Settings.getInstance().getDevProxyPort()); |
||||
|
||||
SecureRequestCustomizer src = new SecureRequestCustomizer(); |
||||
httpConfig.addCustomizer(src); |
||||
|
||||
HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpConfig); |
||||
SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); |
||||
|
||||
ServerConnector portUnifiedConnector = new ServerConnector(this.server, |
||||
new DetectorConnectionFactory(sslConnectionFactory), |
||||
httpConnectionFactory); |
||||
portUnifiedConnector.setHost(Network.getInstance().getBindAddress()); |
||||
portUnifiedConnector.setPort(Settings.getInstance().getDevProxyPort()); |
||||
|
||||
this.server.addConnector(portUnifiedConnector); |
||||
} else { |
||||
// Non-SSL
|
||||
InetAddress bindAddr = InetAddress.getByName(Network.getInstance().getBindAddress()); |
||||
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getDevProxyPort()); |
||||
this.server = new Server(endpoint); |
||||
} |
||||
|
||||
// Error handler
|
||||
ErrorHandler errorHandler = new ApiErrorHandler(); |
||||
this.server.setErrorHandler(errorHandler); |
||||
|
||||
// Request logging
|
||||
if (Settings.getInstance().isDevProxyLoggingEnabled()) { |
||||
RequestLogWriter logWriter = new RequestLogWriter("devproxy-requests.log"); |
||||
logWriter.setAppend(true); |
||||
logWriter.setTimeZone("UTC"); |
||||
RequestLog requestLog = new CustomRequestLog(logWriter, CustomRequestLog.EXTENDED_NCSA_FORMAT); |
||||
this.server.setRequestLog(requestLog); |
||||
} |
||||
|
||||
// Access handler (currently no whitelist is used)
|
||||
InetAccessHandler accessHandler = new InetAccessHandler(); |
||||
this.server.setHandler(accessHandler); |
||||
|
||||
// URL rewriting
|
||||
RewriteHandler rewriteHandler = new RewriteHandler(); |
||||
accessHandler.setHandler(rewriteHandler); |
||||
|
||||
// Context
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS); |
||||
context.setContextPath("/"); |
||||
rewriteHandler.setHandler(context); |
||||
|
||||
// Cross-origin resource sharing
|
||||
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class); |
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*"); |
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE"); |
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false"); |
||||
context.addFilter(corsFilterHolder, "/*", null); |
||||
|
||||
// API servlet
|
||||
ServletContainer container = new ServletContainer(this.config); |
||||
ServletHolder apiServlet = new ServletHolder(container); |
||||
apiServlet.setInitOrder(1); |
||||
context.addServlet(apiServlet, "/*"); |
||||
|
||||
// Start server
|
||||
this.server.start(); |
||||
} catch (Exception e) { |
||||
// Failed to start
|
||||
throw new DataException("Failed to start developer proxy", e); |
||||
} |
||||
} |
||||
|
||||
public void stop() { |
||||
try { |
||||
// Stop server
|
||||
this.server.stop(); |
||||
} catch (Exception e) { |
||||
// Failed to stop
|
||||
} |
||||
|
||||
this.server = null; |
||||
instance = null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,142 @@
|
||||
package org.qortal.api.proxy.resource; |
||||
|
||||
import org.qortal.api.ApiError; |
||||
import org.qortal.api.ApiExceptionFactory; |
||||
import org.qortal.api.HTMLParser; |
||||
import org.qortal.arbitrary.misc.Service; |
||||
import org.qortal.controller.DevProxyManager; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.ws.rs.GET; |
||||
import javax.ws.rs.Path; |
||||
import javax.ws.rs.PathParam; |
||||
import javax.ws.rs.core.Context; |
||||
import java.io.ByteArrayOutputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import java.net.HttpURLConnection; |
||||
import java.net.URL; |
||||
import java.util.Enumeration; |
||||
|
||||
|
||||
@Path("/") |
||||
public class DevProxyServerResource { |
||||
|
||||
@Context HttpServletRequest request; |
||||
@Context HttpServletResponse response; |
||||
@Context ServletContext context; |
||||
|
||||
|
||||
@GET |
||||
public HttpServletResponse getProxyIndex() { |
||||
return this.proxy("/"); |
||||
} |
||||
|
||||
@GET |
||||
@Path("{path:.*}") |
||||
public HttpServletResponse getProxyPath(@PathParam("path") String inPath) { |
||||
return this.proxy(inPath); |
||||
} |
||||
|
||||
private HttpServletResponse proxy(String inPath) { |
||||
try { |
||||
String source = DevProxyManager.getInstance().getSourceHostAndPort(); |
||||
|
||||
// Convert localhost / 127.0.0.1 to IPv6 [::1]
|
||||
if (source.startsWith("localhost") || source.startsWith("127.0.0.1")) { |
||||
int port = 80; |
||||
String[] parts = source.split(":"); |
||||
if (parts.length > 1) { |
||||
port = Integer.parseInt(parts[1]); |
||||
} |
||||
source = String.format("[::1]:%d", port); |
||||
} |
||||
|
||||
if (!inPath.startsWith("/")) { |
||||
inPath = "/" + inPath; |
||||
} |
||||
|
||||
String queryString = request.getQueryString() != null ? "?" + request.getQueryString() : ""; |
||||
|
||||
// Open URL
|
||||
String urlString = String.format("http://%s%s%s", source, inPath, queryString); |
||||
URL url = new URL(urlString); |
||||
HttpURLConnection con = (HttpURLConnection) url.openConnection(); |
||||
con.setRequestMethod(request.getMethod()); |
||||
|
||||
// Proxy the request headers
|
||||
Enumeration<String> headerNames = request.getHeaderNames(); |
||||
while (headerNames.hasMoreElements()) { |
||||
String headerName = headerNames.nextElement(); |
||||
String headerValue = request.getHeader(headerName); |
||||
con.setRequestProperty(headerName, headerValue); |
||||
} |
||||
|
||||
// TODO: proxy any POST parameters from "request" to "con"
|
||||
|
||||
// Proxy the response code
|
||||
int responseCode = con.getResponseCode(); |
||||
response.setStatus(responseCode); |
||||
|
||||
// Proxy the response headers
|
||||
for (int i = 0; ; i++) { |
||||
String headerKey = con.getHeaderFieldKey(i); |
||||
String headerValue = con.getHeaderField(i); |
||||
if (headerKey != null && headerValue != null) { |
||||
response.addHeader(headerKey, headerValue); |
||||
continue; |
||||
} |
||||
break; |
||||
} |
||||
|
||||
// Read the response body
|
||||
InputStream inputStream = con.getInputStream(); |
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); |
||||
byte[] buffer = new byte[4096]; |
||||
int bytesRead; |
||||
while ((bytesRead = inputStream.read(buffer)) != -1) { |
||||
outputStream.write(buffer, 0, bytesRead); |
||||
} |
||||
byte[] data = outputStream.toByteArray(); // TODO: limit file size that can be read into memory
|
||||
|
||||
// Close the streams
|
||||
outputStream.close(); |
||||
inputStream.close(); |
||||
|
||||
// Extract filename
|
||||
String filename = ""; |
||||
if (inPath.contains("/")) { |
||||
String[] parts = inPath.split("/"); |
||||
if (parts.length > 0) { |
||||
filename = parts[parts.length - 1]; |
||||
} |
||||
} |
||||
|
||||
// Parse and modify output if needed
|
||||
if (HTMLParser.isHtmlFile(filename)) { |
||||
// HTML file - needs to be parsed
|
||||
HTMLParser htmlParser = new HTMLParser("", inPath, "", false, data, "proxy", Service.APP, null, "light", true); |
||||
htmlParser.addAdditionalHeaderTags(); |
||||
response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' ws:; font-src 'self' data:;"); |
||||
response.setContentType(con.getContentType()); |
||||
response.setContentLength(htmlParser.getData().length); |
||||
response.getOutputStream().write(htmlParser.getData()); |
||||
} |
||||
else { |
||||
// Regular file - can be streamed directly
|
||||
response.addHeader("Content-Security-Policy", "default-src 'self'"); |
||||
response.setContentType(con.getContentType()); |
||||
response.setContentLength(data.length); |
||||
response.getOutputStream().write(data); |
||||
} |
||||
|
||||
} catch (IOException e) { |
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, e.getMessage()); |
||||
} |
||||
|
||||
return response; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,96 @@
|
||||
package org.qortal.api.resource; |
||||
|
||||
import io.swagger.v3.oas.annotations.Operation; |
||||
import io.swagger.v3.oas.annotations.media.Content; |
||||
import io.swagger.v3.oas.annotations.media.Schema; |
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody; |
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse; |
||||
import io.swagger.v3.oas.annotations.tags.Tag; |
||||
import org.qortal.api.ApiError; |
||||
import org.qortal.api.ApiErrors; |
||||
import org.qortal.api.ApiExceptionFactory; |
||||
import org.qortal.controller.DevProxyManager; |
||||
import org.qortal.repository.DataException; |
||||
|
||||
import javax.servlet.ServletContext; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import javax.ws.rs.POST; |
||||
import javax.ws.rs.Path; |
||||
import javax.ws.rs.core.Context; |
||||
import javax.ws.rs.core.MediaType; |
||||
|
||||
|
||||
@Path("/developer") |
||||
@Tag(name = "Developer Tools") |
||||
public class DeveloperResource { |
||||
|
||||
@Context HttpServletRequest request; |
||||
@Context HttpServletResponse response; |
||||
@Context ServletContext context; |
||||
|
||||
|
||||
@POST |
||||
@Path("/proxy/start") |
||||
@Operation( |
||||
summary = "Start proxy server, for real time QDN app/website development", |
||||
requestBody = @RequestBody( |
||||
description = "Host and port of source webserver to be proxied", |
||||
required = true, |
||||
content = @Content( |
||||
mediaType = MediaType.TEXT_PLAIN, |
||||
schema = @Schema( |
||||
type = "string", |
||||
example = "127.0.0.1:5173" |
||||
) |
||||
) |
||||
), |
||||
responses = { |
||||
@ApiResponse( |
||||
description = "Port number of running server", |
||||
content = @Content( |
||||
mediaType = MediaType.TEXT_PLAIN, |
||||
schema = @Schema( |
||||
type = "number" |
||||
) |
||||
) |
||||
) |
||||
} |
||||
) |
||||
@ApiErrors({ApiError.INVALID_CRITERIA}) |
||||
public Integer startProxy(String sourceHostAndPort) { |
||||
// TODO: API key
|
||||
DevProxyManager devProxyManager = DevProxyManager.getInstance(); |
||||
try { |
||||
devProxyManager.setSourceHostAndPort(sourceHostAndPort); |
||||
devProxyManager.start(); |
||||
return devProxyManager.getPort(); |
||||
|
||||
} catch (DataException e) { |
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, e.getMessage()); |
||||
} |
||||
} |
||||
|
||||
@POST |
||||
@Path("/proxy/stop") |
||||
@Operation( |
||||
summary = "Stop proxy server", |
||||
responses = { |
||||
@ApiResponse( |
||||
description = "true if stopped", |
||||
content = @Content( |
||||
mediaType = MediaType.TEXT_PLAIN, |
||||
schema = @Schema( |
||||
type = "boolean" |
||||
) |
||||
) |
||||
) |
||||
} |
||||
) |
||||
public boolean stopProxy() { |
||||
DevProxyManager devProxyManager = DevProxyManager.getInstance(); |
||||
devProxyManager.stop(); |
||||
return !devProxyManager.isRunning(); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,74 @@
|
||||
package org.qortal.controller; |
||||
|
||||
import org.apache.logging.log4j.LogManager; |
||||
import org.apache.logging.log4j.Logger; |
||||
import org.qortal.api.DevProxyService; |
||||
import org.qortal.repository.DataException; |
||||
import org.qortal.settings.Settings; |
||||
|
||||
public class DevProxyManager { |
||||
|
||||
protected static final Logger LOGGER = LogManager.getLogger(DevProxyManager.class); |
||||
|
||||
private static DevProxyManager instance; |
||||
|
||||
private boolean running = false; |
||||
|
||||
private String sourceHostAndPort = "127.0.0.1:5173"; // Default for React/Vite
|
||||
|
||||
private DevProxyManager() { |
||||
|
||||
} |
||||
|
||||
public static DevProxyManager getInstance() { |
||||
if (instance == null) |
||||
instance = new DevProxyManager(); |
||||
|
||||
return instance; |
||||
} |
||||
|
||||
public void start() throws DataException { |
||||
synchronized(this) { |
||||
if (this.running) { |
||||
// Already running
|
||||
return; |
||||
} |
||||
|
||||
LOGGER.info(String.format("Starting developer proxy service on port %d", Settings.getInstance().getDevProxyPort())); |
||||
DevProxyService devProxyService = DevProxyService.getInstance(); |
||||
devProxyService.start(); |
||||
this.running = true; |
||||
} |
||||
} |
||||
|
||||
public void stop() { |
||||
synchronized(this) { |
||||
if (!this.running) { |
||||
// Not running
|
||||
return; |
||||
} |
||||
|
||||
LOGGER.info(String.format("Shutting down developer proxy service")); |
||||
DevProxyService devProxyService = DevProxyService.getInstance(); |
||||
devProxyService.stop(); |
||||
this.running = false; |
||||
} |
||||
} |
||||
|
||||
public void setSourceHostAndPort(String sourceHostAndPort) { |
||||
this.sourceHostAndPort = sourceHostAndPort; |
||||
} |
||||
|
||||
public String getSourceHostAndPort() { |
||||
return this.sourceHostAndPort; |
||||
} |
||||
|
||||
public Integer getPort() { |
||||
return Settings.getInstance().getDevProxyPort(); |
||||
} |
||||
|
||||
public boolean isRunning() { |
||||
return this.running; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,83 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) |
||||
# Keys are from api.ApiError enum |
||||
|
||||
# "localeLang": "jp", |
||||
|
||||
### Common ### |
||||
JSON = JSON メッセージの解析に失敗しました |
||||
|
||||
INSUFFICIENT_BALANCE = 残高不足 |
||||
|
||||
UNAUTHORIZED = APIコール未承認 |
||||
|
||||
REPOSITORY_ISSUE = リポジトリエラー |
||||
|
||||
NON_PRODUCTION = この APIコールはプロダクションシステムでは許可されていません |
||||
|
||||
BLOCKCHAIN_NEEDS_SYNC = ブロックチェーンをまず同期する必要があります |
||||
|
||||
NO_TIME_SYNC = 時刻が未同期 |
||||
|
||||
### Validation ### |
||||
INVALID_SIGNATURE = 無効な署名 |
||||
|
||||
INVALID_ADDRESS = 無効なアドレス |
||||
|
||||
INVALID_PUBLIC_KEY = 無効な公開鍵 |
||||
|
||||
INVALID_DATA = 無効なデータ |
||||
|
||||
INVALID_NETWORK_ADDRESS = 無効なネットワーク アドレス |
||||
|
||||
ADDRESS_UNKNOWN = 不明なアカウントアドレス |
||||
|
||||
INVALID_CRITERIA = 無効な検索条件 |
||||
|
||||
INVALID_REFERENCE = 無効な参照 |
||||
|
||||
TRANSFORMATION_ERROR = JSONをトランザクションに変換出来ませんでした |
||||
|
||||
INVALID_PRIVATE_KEY = 無効な秘密鍵 |
||||
|
||||
INVALID_HEIGHT = 無効なブロック高 |
||||
|
||||
CANNOT_MINT = アカウントはミント出来ません |
||||
|
||||
### Blocks ### |
||||
BLOCK_UNKNOWN = 不明なブロック |
||||
|
||||
### Transactions ### |
||||
TRANSACTION_UNKNOWN = 不明なトランザクション |
||||
|
||||
PUBLIC_KEY_NOT_FOUND = 公開鍵が見つかりません |
||||
|
||||
# this one is special in that caller expected to pass two additional strings, hence the two %s |
||||
TRANSACTION_INVALID = 無効なトランザクション: %s (%s) |
||||
|
||||
### Naming ### |
||||
NAME_UNKNOWN = 不明な名前 |
||||
|
||||
### Asset ### |
||||
INVALID_ASSET_ID = 無効なアセット ID |
||||
|
||||
INVALID_ORDER_ID = 無効なアセット注文 ID |
||||
|
||||
ORDER_UNKNOWN = 不明なアセット注文 ID |
||||
|
||||
### Groups ### |
||||
GROUP_UNKNOWN = 不明なグループ |
||||
|
||||
### Foreign Blockchain ### |
||||
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = 外部ブロックチェーンまたはElectrumXネットワークの問題 |
||||
|
||||
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = 外部ブロックチェーンの残高が不足しています |
||||
|
||||
FOREIGN_BLOCKCHAIN_TOO_SOON = 外部ブロックチェーン トランザクションのブロードキャストが時期尚早 (ロックタイム/ブロック時間の中央値) |
||||
|
||||
### Trade Portal ### |
||||
ORDER_SIZE_TOO_SMALL = 注文金額が低すぎます |
||||
|
||||
### Data ### |
||||
FILE_NOT_FOUND = ファイルが見つかりません |
||||
|
||||
NO_REPLY = ピアが制限時間内に応答しませんでした |
@ -0,0 +1,48 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/) |
||||
# SysTray pop-up menu # Japanese translation by R M 2023 |
||||
|
||||
APPLYING_UPDATE_AND_RESTARTING = 自動更新を適用して再起動しています... |
||||
|
||||
AUTO_UPDATE = 自動更新 |
||||
|
||||
BLOCK_HEIGHT = ブロック高 |
||||
|
||||
BLOCKS_REMAINING = 残りのブロック |
||||
|
||||
BUILD_VERSION = ビルドバージョン |
||||
|
||||
CHECK_TIME_ACCURACY = 時刻の精度を確認 |
||||
|
||||
CONNECTING = 接続中 |
||||
|
||||
CONNECTION = 接続 |
||||
|
||||
CONNECTIONS = 接続 |
||||
|
||||
CREATING_BACKUP_OF_DB_FILES = データベース ファイルのバックアップを作成中... |
||||
|
||||
DB_BACKUP = データベースのバックアップ |
||||
|
||||
DB_CHECKPOINT = データベースのチェックポイント |
||||
|
||||
DB_MAINTENANCE = データベースのメンテナンス |
||||
|
||||
EXIT = 終了 |
||||
|
||||
LITE_NODE = ライトノード |
||||
|
||||
MINTING_DISABLED = ミント一時中止中 |
||||
|
||||
MINTING_ENABLED = \u2714 ミント |
||||
|
||||
OPEN_UI = UIを開く |
||||
|
||||
PERFORMING_DB_CHECKPOINT = コミットされていないデータベースの変更を保存中... |
||||
|
||||
PERFORMING_DB_MAINTENANCE = 定期メンテナンスを実行中... |
||||
|
||||
SYNCHRONIZE_CLOCK = 時刻を同期 |
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = ブロックチェーンを同期中 |
||||
|
||||
SYNCHRONIZING_CLOCK = 時刻を同期中 |
@ -0,0 +1,195 @@
|
||||
# |
||||
|
||||
ACCOUNT_ALREADY_EXISTS = 既にアカウントは存在します |
||||
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = アカウントは報酬シェアが出来ません |
||||
|
||||
ADDRESS_ABOVE_RATE_LIMIT = アドレスが指定されたレート制限に達しました |
||||
|
||||
ADDRESS_BLOCKED = このアドレスはブロックされています |
||||
|
||||
ALREADY_GROUP_ADMIN = 既ににグループ管理者です |
||||
|
||||
ALREADY_GROUP_MEMBER = 既にグループメンバーです |
||||
|
||||
ALREADY_VOTED_FOR_THAT_OPTION = 既にそのオプションに投票しています |
||||
|
||||
ASSET_ALREADY_EXISTS = 既にアセットは存在します |
||||
|
||||
ASSET_DOES_NOT_EXIST = アセットが存在しません |
||||
|
||||
ASSET_DOES_NOT_MATCH_AT = アセットがATのアセットと一致しません |
||||
|
||||
ASSET_NOT_SPENDABLE = 資産が使用不可です |
||||
|
||||
AT_ALREADY_EXISTS = 既にATが存在します |
||||
|
||||
AT_IS_FINISHED = ATが終了しました |
||||
|
||||
AT_UNKNOWN = 不明なAT |
||||
|
||||
BAN_EXISTS = 既にバンされてます |
||||
|
||||
BAN_UNKNOWN = 不明なバン |
||||
|
||||
BANNED_FROM_GROUP = グループからのバンされています |
||||
|
||||
BUYER_ALREADY_OWNER = 既に購入者が所有者です |
||||
|
||||
CLOCK_NOT_SYNCED = 時刻が未同期 |
||||
|
||||
DUPLICATE_MESSAGE = このアドレスは重複メッセージを送信しました |
||||
|
||||
DUPLICATE_OPTION = 重複したオプション |
||||
|
||||
GROUP_ALREADY_EXISTS = 既にグループは存在します |
||||
|
||||
GROUP_APPROVAL_DECIDED = 既にグループの承認は決定されています |
||||
|
||||
GROUP_APPROVAL_NOT_REQUIRED = グループ承認が不必要 |
||||
|
||||
GROUP_DOES_NOT_EXIST = グループが存在しません |
||||
|
||||
GROUP_ID_MISMATCH = グループ ID が不一致 |
||||
|
||||
GROUP_OWNER_CANNOT_LEAVE = グループ所有者はグループを退会出来ません |
||||
|
||||
HAVE_EQUALS_WANT = 持っている資産は欲しい資産と同じです |
||||
|
||||
INCORRECT_NONCE = 不正な PoW ナンス |
||||
|
||||
INSUFFICIENT_FEE = 手数料が不十分です |
||||
|
||||
INVALID_ADDRESS = 無効なアドレス |
||||
|
||||
INVALID_AMOUNT = 無効な金額 |
||||
|
||||
INVALID_ASSET_OWNER = 無効なアセット所有者 |
||||
|
||||
INVALID_AT_TRANSACTION = 無効なATトランザクション |
||||
|
||||
INVALID_AT_TYPE_LENGTH = 無効なATの「タイプ」の長さです |
||||
|
||||
INVALID_BUT_OK = 無効だがOK |
||||
|
||||
INVALID_CREATION_BYTES = 無効な作成バイト数 |
||||
|
||||
INVALID_DATA_LENGTH = 無効なデータ長 |
||||
|
||||
INVALID_DESCRIPTION_LENGTH = 無効な概要の長さ |
||||
|
||||
INVALID_GROUP_APPROVAL_THRESHOLD = 無効なグループ承認のしきい値 |
||||
|
||||
INVALID_GROUP_BLOCK_DELAY = 無効なグループ承認のブロック遅延 |
||||
|
||||
INVALID_GROUP_ID = 無効なグループ ID |
||||
|
||||
INVALID_GROUP_OWNER = 無効なグループ所有者 |
||||
|
||||
INVALID_LIFETIME = 無効な有効期間 |
||||
|
||||
INVALID_NAME_LENGTH = 無効な名前の長さです |
||||
|
||||
INVALID_NAME_OWNER = 無効な名前の所有者 |
||||
|
||||
INVALID_OPTION_LENGTH = 無効なオプションの長さ |
||||
|
||||
INVALID_OPTIONS_COUNT = 無効なオプションの数 |
||||
|
||||
INVALID_ORDER_CREATOR = 無効な注文作成者 |
||||
|
||||
INVALID_PAYMENTS_COUNT = 無効な入出金数 |
||||
|
||||
INVALID_PUBLIC_KEY = 無効な公開鍵 |
||||
|
||||
INVALID_QUANTITY = 無効な数量 |
||||
|
||||
INVALID_REFERENCE = 無効な参照 |
||||
|
||||
INVALID_RETURN = 無効な返品 |
||||
|
||||
INVALID_REWARD_SHARE_PERCENT = 無効な報酬シェア率 |
||||
|
||||
INVALID_SELLER = 無効な販売者 |
||||
|
||||
INVALID_TAGS_LENGTH = 無効な「タグ」の長さ |
||||
|
||||
INVALID_TIMESTAMP_SIGNATURE = 無効なタイムスタンプ署名 |
||||
|
||||
INVALID_TX_GROUP_ID = 無効なトランザクション グループ ID |
||||
|
||||
INVALID_VALUE_LENGTH = 無効な「値」の長さ |
||||
|
||||
INVITE_UNKNOWN = 不明なグループ招待 |
||||
|
||||
JOIN_REQUEST_EXISTS = 既にグループ参加リクエストが存在します |
||||
|
||||
MAXIMUM_REWARD_SHARES = 既にこのアカウントの報酬シェアは最大です |
||||
|
||||
MISSING_CREATOR = 作成者が見つかりません |
||||
|
||||
MULTIPLE_NAMES_FORBIDDEN = アカウントごとに複数の登録名は禁止されています |
||||
|
||||
NAME_ALREADY_FOR_SALE = 既に名前は販売中です |
||||
|
||||
NAME_ALREADY_REGISTERED = 既に名前は登録されています |
||||
|
||||
NAME_BLOCKED = この名前はブロックされています |
||||
|
||||
NAME_DOES_NOT_EXIST = 名前は存在しません |
||||
|
||||
NAME_NOT_FOR_SALE = 名前は非売品です |
||||
|
||||
NAME_NOT_NORMALIZED = 名前は Unicode の「正規化」形式ではありません |
||||
|
||||
NEGATIVE_AMOUNT = 無効な/負の金額 |
||||
|
||||
NEGATIVE_FEE = 無効な/負の料金 |
||||
|
||||
NEGATIVE_PRICE = 無効な/負の価格 |
||||
|
||||
NO_BALANCE = 残高が不足しています |
||||
|
||||
NO_BLOCKCHAIN_LOCK = ノードのブロックチェーンは現在ビジーです |
||||
|
||||
NO_FLAG_PERMISSION = アカウントにはその権限がありません |
||||
|
||||
NOT_GROUP_ADMIN = アカウントはグループ管理者ではありません |
||||
|
||||
NOT_GROUP_MEMBER = アカウントはグループメンバーではありません |
||||
|
||||
NOT_MINTING_ACCOUNT = アカウントはミント出来ません |
||||
|
||||
NOT_YET_RELEASED = 機能はまだリリースされていません |
||||
|
||||
OK = OK |
||||
|
||||
ORDER_ALREADY_CLOSED = 既に資産取引注文は終了しています |
||||
|
||||
ORDER_DOES_NOT_EXIST = 資産取引注文が存在しません |
||||
|
||||
POLL_ALREADY_EXISTS = 既に投票は存在します |
||||
|
||||
POLL_DOES_NOT_EXIST = 投票は存在しません |
||||
|
||||
POLL_OPTION_DOES_NOT_EXIST = 投票オプションが存在しません |
||||
|
||||
PUBLIC_KEY_UNKNOWN = 不明な公開鍵 |
||||
|
||||
REWARD_SHARE_UNKNOWN = 不明な報酬シェア |
||||
|
||||
SELF_SHARE_EXISTS = 既に自己シェア(報酬シェア)が存在します |
||||
|
||||
TIMESTAMP_TOO_NEW = タイムスタンプが新しすぎます |
||||
|
||||
TIMESTAMP_TOO_OLD = タイムスタンプが古すぎます |
||||
|
||||
TOO_MANY_UNCONFIRMED = アカウントに保留中の未承認トランザクションが多すぎます |
||||
|
||||
TRANSACTION_ALREADY_CONFIRMED = 既にトランザクションは承認されています |
||||
|
||||
TRANSACTION_ALREADY_EXISTS = 既にトランザクションは存在します |
||||
|
||||
TRANSACTION_UNKNOWN = 不明なトランザクション |
||||
|
||||
TX_GROUP_ID_MISMATCH = トランザクションのグループIDが一致しません |
Loading…
Reference in new issue