فهرست منبع

Fix upload file to QiNiu bug
Finished upload function
Finished Menu Settings

zhuzhuyule 7 سال پیش
والد
کامیت
71662d276b

+ 11 - 14
views/main/hexo-qiniu.js → app/hexo-qiniu.js

@@ -1,6 +1,6 @@
 class qiniuServer {
     constructor(acessKey, secretKey) {
-        this.qiniu = require('./qiniu/index');
+        this.qiniu = require('qiniu');
         this.bucket = '';
         this.qiniu.conf.ACCESS_KEY = acessKey;
         this.qiniu.conf.SECRET_KEY = secretKey;
@@ -50,14 +50,13 @@ class qiniuServer {
      */
     getBuckets(callback) {
         const url_api_bukets = 'https://rs.qbox.me/buckets';
+        let XMLHttpRequest = require('./tool/XMLHttpRequest').XMLHttpRequest;
         let xhr = new XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
         xhr.onreadystatechange = () => {
             if (xhr.readyState === 4) { // 成功完成
-                console.log(xhr.status)
-                console.log(xhr.responseText)
                 if (typeof callback === "function") {
                     callback({
                         code: xhr.status,
@@ -75,15 +74,13 @@ class qiniuServer {
      */
     getBucketsUrl(buketName,callback) {
         const url_api_bukets = 'https://api.qiniu.com/v6/domain/list?tbl=' + buketName;
-
+        let XMLHttpRequest = require('./tool/XMLHttpRequest').XMLHttpRequest;
         let xhr = new XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
         xhr.onreadystatechange = () => {
             if (xhr.readyState === 4) { // 成功完成
-                console.log(xhr.status)
-                console.log(xhr.responseText)
                 if (typeof callback === "function") {
                     callback({
                         code: xhr.status,
@@ -104,14 +101,13 @@ class qiniuServer {
         if (!buketName) return;
         const url_api_bukets = require('util').format(
             'https://rsf.qbox.me/list?bucket=%s&marker=&limit=1&prefix=%s&delimiter=/', buketName, prefix || '')
+        let XMLHttpRequest = require('./tool/XMLHttpRequest').XMLHttpRequest;
         let xhr = new XMLHttpRequest();
         xhr.open('get', url_api_bukets);
         xhr.setRequestHeader('Authorization', this.getAccessToken(url_api_bukets));
         xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
         xhr.onreadystatechange = () => {
             if (xhr.readyState === 4) { // 成功完成
-                console.log(xhr.status)
-                console.log(xhr.responseText)
                 if (typeof callback === "function") {
                     callback({
                         code: xhr.status,
@@ -136,20 +132,21 @@ class qiniuServer {
         var extra = new this.qiniu.form_up.PutExtra();
         let qiniuServer = this;
         formUploader.putFile(token, serverFileName, localFile, extra,
-            function (statusCode,response, responseText) {
-                console.log( response);
-                if (statusCode == 200 || statusCode == 579) {
+            function (error,data, responseInfo) {
+                if (error)
+                    console.log( error);
+                if (responseInfo.statusCode == 200 || responseInfo.statusCode == 579) {
                     let result = {
                         code: 'success',
                         data: {
-                            url: qiniuServer.url + response.key
+                            url: qiniuServer.url + data.key
                         }
                     }
                     callback(localFile, result);
                 } else {
-                    console.log(statusCode);
+                    console.log(responseInfo.statusCode);
                     let result = {
-                        error: statusCode + responseText,
+                        error: responseInfo.statusCode + responseInfo,
                     }
                     callback(localFile, result);
                 }

+ 2 - 0
app/moe-app.js

@@ -27,6 +27,7 @@ const MoeditorWindow = require('./moe-window'),
       shortcut = require('electron-localshortcut'),
       MoeditorLocale = require('./moe-l10n'),
       MoeditorShell = require('./moe-shell'),
+      QiniuServer = require('./hexo-qiniu'),
       MoeditorAbout = require('./moe-about'),
       MoeditorSettings = require('./moe-settings'),
       fs = require('fs'),
@@ -57,6 +58,7 @@ class MoeditorApplication {
 
         this.locale = new MoeditorLocale();
         this.shellServer = new MoeditorShell();
+        this.qiniuServer = new QiniuServer();
         global.__ = str => this.locale.get(str);
 
         this.flag = new Object();

+ 1 - 1
app/moe-config-default.js

@@ -50,6 +50,6 @@ module.exports = {
     'image-qiniu-accessKey':'',
     'image-qiniu-secretKey':'',
     'image-qiniu-bucket':'',
-    'image-qiniu-url-protocol': 'https://',
+    'image-qiniu-url-protocol': 'http://',
     'image-qiniu-url':'',
 };

+ 14 - 10
app/moe-l10n.js

@@ -229,15 +229,17 @@ const strings = {
         "Delete": "Delete",
         "Select All": "Select All",
 
+        "UploadToSMMS": "Upload To SM.MS",
+        "UploadToQiNiu": "Upload To QiNiu",
         "Quick Open":"Quick Open",
         "OpenPathPost":"Open Post Path",
         "OpenPathPostSrc":"Open Post Src Path",
-        "OpenPathSrcCenter":"Open Src Center Path",
-        "OpenPathHEXO":"Open Work Path",
+        "OpenPathSrcCenter":"Open All Src Path",
+        "OpenPathHEXO":"Open Hexo Path",
         "WebIndex":"Open Your Index",
-        "WebLocalIndex":"Open Server Index",
-        "WebQiNiuSource":"Open QiNiu Source Index",
-        "WebSMMS":"Open SM.MS",
+        "WebLocalIndex":"Open Local Index",
+        "WebQiNiuSource":"Open QiNiu Src Index",
+        "WebSMMS":"Open SM.MS Index",
 
         "Services": "Services",
         "Hide": "Hide",
@@ -397,14 +399,16 @@ const strings = {
         "Delete": "删除",
         "Select All": "全选",
 
+        "UploadToSMMS": "上传SM.MS",
+        "UploadToQiNiu": "上传七牛",
         "Quick Open":"快速打开",
-        "OpenPathPost":"编辑文件",
-        "OpenPathPostSrc":"文件引用资源",
-        "OpenPathSrcCenter":"资源中心",
-        "OpenPathHEXO":"Hexo项目",
+        "OpenPathPost":"本文所在路径",
+        "OpenPathPostSrc":"本文图片路径",
+        "OpenPathSrcCenter":"全部图片路径",
+        "OpenPathHEXO":"Hexo项目路径",
         "WebIndex":"主页",
         "WebLocalIndex":"本地主页",
-        "WebQiNiuSource":"七牛存储空间",
+        "WebQiNiuSource":"七牛存储主页",
         "WebSMMS":"SM.MS主页",
 
         "Services": "服务",

+ 620 - 0
app/tool/XMLHttpRequest.js

@@ -0,0 +1,620 @@
+/**
+ * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object.
+ *
+ * This can be used with JS designed for browsers to improve reuse of code and
+ * allow the use of existing libraries.
+ *
+ * Usage: include("XMLHttpRequest.js") and use XMLHttpRequest per W3C specs.
+ *
+ * @author Dan DeFelippi <dan@driverdan.com>
+ * @contributor David Ellis <d.f.ellis@ieee.org>
+ * @license MIT
+ */
+
+var Url = require("url");
+var spawn = require("child_process").spawn;
+var fs = require("fs");
+
+exports.XMLHttpRequest = function() {
+    "use strict";
+
+    /**
+     * Private variables
+     */
+    var self = this;
+    var http = require("http");
+    var https = require("https");
+
+    // Holds http.js objects
+    var request;
+    var response;
+
+    // Request settings
+    var settings = {};
+
+    // Disable header blacklist.
+    // Not part of XHR specs.
+    var disableHeaderCheck = false;
+
+    // Set some default headers
+    var defaultHeaders = {
+        "User-Agent": "node-XMLHttpRequest",
+        "Accept": "*/*",
+    };
+
+    var headers = {};
+    var headersCase = {};
+
+    // These headers are not user setable.
+    // The following are allowed but banned in the spec:
+    // * user-agent
+    var forbiddenRequestHeaders = [
+        "accept-charset",
+        "accept-encoding",
+        "access-control-request-headers",
+        "access-control-request-method",
+        "connection",
+        "content-length",
+        "content-transfer-encoding",
+        "cookie",
+        "cookie2",
+        "date",
+        "expect",
+        "host",
+        "keep-alive",
+        "origin",
+        "referer",
+        "te",
+        "trailer",
+        "transfer-encoding",
+        "upgrade",
+        "via"
+    ];
+
+    // These request methods are not allowed
+    var forbiddenRequestMethods = [
+        "TRACE",
+        "TRACK",
+        "CONNECT"
+    ];
+
+    // Send flag
+    var sendFlag = false;
+    // Error flag, used when errors occur or abort is called
+    var errorFlag = false;
+
+    // Event listeners
+    var listeners = {};
+
+    /**
+     * Constants
+     */
+
+    this.UNSENT = 0;
+    this.OPENED = 1;
+    this.HEADERS_RECEIVED = 2;
+    this.LOADING = 3;
+    this.DONE = 4;
+
+    /**
+     * Public vars
+     */
+
+    // Current state
+    this.readyState = this.UNSENT;
+
+    // default ready state change handler in case one is not set or is set late
+    this.onreadystatechange = null;
+
+    // Result & response
+    this.responseText = "";
+    this.responseXML = "";
+    this.status = null;
+    this.statusText = null;
+
+    // Whether cross-site Access-Control requests should be made using
+    // credentials such as cookies or authorization headers
+    this.withCredentials = false;
+
+    /**
+     * Private methods
+     */
+
+    /**
+     * Check if the specified header is allowed.
+     *
+     * @param string header Header to validate
+     * @return boolean False if not allowed, otherwise true
+     */
+    var isAllowedHttpHeader = function(header) {
+        return disableHeaderCheck || (header && forbiddenRequestHeaders.indexOf(header.toLowerCase()) === -1);
+    };
+
+    /**
+     * Check if the specified method is allowed.
+     *
+     * @param string method Request method to validate
+     * @return boolean False if not allowed, otherwise true
+     */
+    var isAllowedHttpMethod = function(method) {
+        return (method && forbiddenRequestMethods.indexOf(method) === -1);
+    };
+
+    /**
+     * Public methods
+     */
+
+    /**
+     * Open the connection. Currently supports local server requests.
+     *
+     * @param string method Connection method (eg GET, POST)
+     * @param string url URL for the connection.
+     * @param boolean async Asynchronous connection. Default is true.
+     * @param string user Username for basic authentication (optional)
+     * @param string password Password for basic authentication (optional)
+     */
+    this.open = function(method, url, async, user, password) {
+        this.abort();
+        errorFlag = false;
+
+        // Check for valid request method
+        if (!isAllowedHttpMethod(method)) {
+            throw new Error("SecurityError: Request method not allowed");
+        }
+
+        settings = {
+            "method": method,
+            "url": url.toString(),
+            "async": (typeof async !== "boolean" ? true : async),
+            "user": user || null,
+            "password": password || null
+        };
+
+        setState(this.OPENED);
+    };
+
+    /**
+     * Disables or enables isAllowedHttpHeader() check the request. Enabled by default.
+     * This does not conform to the W3C spec.
+     *
+     * @param boolean state Enable or disable header checking.
+     */
+    this.setDisableHeaderCheck = function(state) {
+        disableHeaderCheck = state;
+    };
+
+    /**
+     * Sets a header for the request or appends the value if one is already set.
+     *
+     * @param string header Header name
+     * @param string value Header value
+     */
+    this.setRequestHeader = function(header, value) {
+        if (this.readyState !== this.OPENED) {
+            throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN");
+        }
+        if (!isAllowedHttpHeader(header)) {
+            console.warn("Refused to set unsafe header \"" + header + "\"");
+            return;
+        }
+        if (sendFlag) {
+            throw new Error("INVALID_STATE_ERR: send flag is true");
+        }
+        header = headersCase[header.toLowerCase()] || header;
+        headersCase[header.toLowerCase()] = header;
+        headers[header] = headers[header] ? headers[header] + ', ' + value : value;
+    };
+
+    /**
+     * Gets a header from the server response.
+     *
+     * @param string header Name of header to get.
+     * @return string Text of the header or null if it doesn't exist.
+     */
+    this.getResponseHeader = function(header) {
+        if (typeof header === "string"
+            && this.readyState > this.OPENED
+            && response
+            && response.headers
+            && response.headers[header.toLowerCase()]
+            && !errorFlag
+        ) {
+            return response.headers[header.toLowerCase()];
+        }
+
+        return null;
+    };
+
+    /**
+     * Gets all the response headers.
+     *
+     * @return string A string with all response headers separated by CR+LF
+     */
+    this.getAllResponseHeaders = function() {
+        if (this.readyState < this.HEADERS_RECEIVED || errorFlag) {
+            return "";
+        }
+        var result = "";
+
+        for (var i in response.headers) {
+            // Cookie headers are excluded
+            if (i !== "set-cookie" && i !== "set-cookie2") {
+                result += i + ": " + response.headers[i] + "\r\n";
+            }
+        }
+        return result.substr(0, result.length - 2);
+    };
+
+    /**
+     * Gets a request header
+     *
+     * @param string name Name of header to get
+     * @return string Returns the request header or empty string if not set
+     */
+    this.getRequestHeader = function(name) {
+        if (typeof name === "string" && headersCase[name.toLowerCase()]) {
+            return headers[headersCase[name.toLowerCase()]];
+        }
+
+        return "";
+    };
+
+    /**
+     * Sends the request to the server.
+     *
+     * @param string data Optional data to send as request body.
+     */
+    this.send = function(data) {
+        if (this.readyState !== this.OPENED) {
+            throw new Error("INVALID_STATE_ERR: connection must be opened before send() is called");
+        }
+
+        if (sendFlag) {
+            throw new Error("INVALID_STATE_ERR: send has already been called");
+        }
+
+        var ssl = false, local = false;
+        var url = Url.parse(settings.url);
+        var host;
+        // Determine the server
+        switch (url.protocol) {
+            case "https:":
+                ssl = true;
+            // SSL & non-SSL both need host, no break here.
+            case "http:":
+                host = url.hostname;
+                break;
+
+            case "file:":
+                local = true;
+                break;
+
+            case undefined:
+            case null:
+            case "":
+                host = "localhost";
+                break;
+
+            default:
+                throw new Error("Protocol not supported.");
+        }
+
+        // Load files off the local filesystem (file://)
+        if (local) {
+            if (settings.method !== "GET") {
+                throw new Error("XMLHttpRequest: Only GET method is supported");
+            }
+
+            if (settings.async) {
+                fs.readFile(url.pathname, "utf8", function(error, data) {
+                    if (error) {
+                        self.handleError(error);
+                    } else {
+                        self.status = 200;
+                        self.responseText = data;
+                        setState(self.DONE);
+                    }
+                });
+            } else {
+                try {
+                    this.responseText = fs.readFileSync(url.pathname, "utf8");
+                    this.status = 200;
+                    setState(self.DONE);
+                } catch(e) {
+                    this.handleError(e);
+                }
+            }
+
+            return;
+        }
+
+        // Default to port 80. If accessing localhost on another port be sure
+        // to use http://localhost:port/path
+        var port = url.port || (ssl ? 443 : 80);
+        // Add query string if one is used
+        var uri = url.pathname + (url.search ? url.search : "");
+
+        // Set the defaults if they haven't been set
+        for (var name in defaultHeaders) {
+            if (!headersCase[name.toLowerCase()]) {
+                headers[name] = defaultHeaders[name];
+            }
+        }
+
+        // Set the Host header or the server may reject the request
+        headers.Host = host;
+        if (!((ssl && port === 443) || port === 80)) {
+            headers.Host += ":" + url.port;
+        }
+
+        // Set Basic Auth if necessary
+        if (settings.user) {
+            if (typeof settings.password === "undefined") {
+                settings.password = "";
+            }
+            var authBuf = new Buffer(settings.user + ":" + settings.password);
+            headers.Authorization = "Basic " + authBuf.toString("base64");
+        }
+
+        // Set content length header
+        if (settings.method === "GET" || settings.method === "HEAD") {
+            data = null;
+        } else if (data) {
+            headers["Content-Length"] = Buffer.isBuffer(data) ? data.length : Buffer.byteLength(data);
+
+            if (!headers["Content-Type"]) {
+                headers["Content-Type"] = "text/plain;charset=UTF-8";
+            }
+        } else if (settings.method === "POST") {
+            // For a post with no data set Content-Length: 0.
+            // This is required by buggy servers that don't meet the specs.
+            headers["Content-Length"] = 0;
+        }
+
+        var options = {
+            host: host,
+            port: port,
+            path: uri,
+            method: settings.method,
+            headers: headers,
+            agent: false,
+            withCredentials: self.withCredentials
+        };
+
+        // Reset error flag
+        errorFlag = false;
+
+        // Handle async requests
+        if (settings.async) {
+            // Use the proper protocol
+            var doRequest = ssl ? https.request : http.request;
+
+            // Request is being sent, set send flag
+            sendFlag = true;
+
+            // As per spec, this is called here for historical reasons.
+            self.dispatchEvent("readystatechange");
+
+            // Handler for the response
+            var responseHandler = function responseHandler(resp) {
+                // Set response var to the response we got back
+                // This is so it remains accessable outside this scope
+                response = resp;
+                // Check for redirect
+                // @TODO Prevent looped redirects
+                if (response.statusCode === 301 || response.statusCode === 302 || response.statusCode === 303 || response.statusCode === 307) {
+                    // Change URL to the redirect location
+                    settings.url = response.headers.location;
+                    var url = Url.parse(settings.url);
+                    // Set host var in case it's used later
+                    host = url.hostname;
+                    // Options for the new request
+                    var newOptions = {
+                        hostname: url.hostname,
+                        port: url.port,
+                        path: url.path,
+                        method: response.statusCode === 303 ? "GET" : settings.method,
+                        headers: headers,
+                        withCredentials: self.withCredentials
+                    };
+
+                    // Issue the new request
+                    request = doRequest(newOptions, responseHandler).on("error", errorHandler);
+                    request.end();
+                    // @TODO Check if an XHR event needs to be fired here
+                    return;
+                }
+
+                response.setEncoding("utf8");
+
+                setState(self.HEADERS_RECEIVED);
+                self.status = response.statusCode;
+
+                response.on("data", function(chunk) {
+                    // Make sure there's some data
+                    if (chunk) {
+                        self.responseText += chunk;
+                    }
+                    // Don't emit state changes if the connection has been aborted.
+                    if (sendFlag) {
+                        setState(self.LOADING);
+                    }
+                });
+
+                response.on("end", function() {
+                    if (sendFlag) {
+                        // Discard the end event if the connection has been aborted
+                        setState(self.DONE);
+                        sendFlag = false;
+                    }
+                });
+
+                response.on("error", function(error) {
+                    self.handleError(error);
+                });
+            };
+
+            // Error handler for the request
+            var errorHandler = function errorHandler(error) {
+                self.handleError(error);
+            };
+
+            // Create the request
+            request = doRequest(options, responseHandler).on("error", errorHandler);
+
+            // Node 0.4 and later won't accept empty data. Make sure it's needed.
+            if (data) {
+                request.write(data);
+            }
+
+            request.end();
+
+            self.dispatchEvent("loadstart");
+        } else { // Synchronous
+            // Create a temporary file for communication with the other Node process
+            var contentFile = ".node-xmlhttprequest-content-" + process.pid;
+            var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
+            fs.writeFileSync(syncFile, "", "utf8");
+            // The async request the other Node process executes
+            var execString = "var http = require('http'), https = require('https'), fs = require('fs');"
+                + "var doRequest = http" + (ssl ? "s" : "") + ".request;"
+                + "var options = " + JSON.stringify(options) + ";"
+                + "var responseText = '';"
+                + "var req = doRequest(options, function(response) {"
+                + "response.setEncoding('utf8');"
+                + "response.on('data', function(chunk) {"
+                + "  responseText += chunk;"
+                + "});"
+                + "response.on('end', function() {"
+                + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText}}), 'utf8');"
+                + "fs.unlinkSync('" + syncFile + "');"
+                + "});"
+                + "response.on('error', function(error) {"
+                + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: error}), 'utf8');"
+                + "fs.unlinkSync('" + syncFile + "');"
+                + "});"
+                + "}).on('error', function(error) {"
+                + "fs.writeFileSync('" + contentFile + "', JSON.stringify({err: error}), 'utf8');"
+                + "fs.unlinkSync('" + syncFile + "');"
+                + "});"
+                + (data ? "req.write('" + JSON.stringify(data).slice(1,-1).replace(/'/g, "\\'") + "');":"")
+                + "req.end();";
+            // Start the other Node Process, executing this string
+            var syncProc = spawn(process.argv[0], ["-e", execString]);
+            while(fs.existsSync(syncFile)) {
+                // Wait while the sync file is empty
+            }
+            var resp = JSON.parse(fs.readFileSync(contentFile, 'utf8'));
+            // Kill the child process once the file has data
+            syncProc.stdin.end();
+            // Remove the temporary file
+            fs.unlinkSync(contentFile);
+
+            if (resp.err) {
+                self.handleError(resp.err);
+            } else {
+                response = resp.data;
+                self.status = resp.data.statusCode;
+                self.responseText = resp.data.text;
+                setState(self.DONE);
+            }
+        }
+    };
+
+    /**
+     * Called when an error is encountered to deal with it.
+     */
+    this.handleError = function(error) {
+        this.status = 0;
+        this.statusText = error;
+        this.responseText = error.stack;
+        errorFlag = true;
+        setState(this.DONE);
+        this.dispatchEvent('error');
+    };
+
+    /**
+     * Aborts a request.
+     */
+    this.abort = function() {
+        if (request) {
+            request.abort();
+            request = null;
+        }
+
+        headers = defaultHeaders;
+        this.status = 0;
+        this.responseText = "";
+        this.responseXML = "";
+
+        errorFlag = true;
+
+        if (this.readyState !== this.UNSENT
+            && (this.readyState !== this.OPENED || sendFlag)
+            && this.readyState !== this.DONE) {
+            sendFlag = false;
+            setState(this.DONE);
+        }
+        this.readyState = this.UNSENT;
+        this.dispatchEvent('abort');
+    };
+
+    /**
+     * Adds an event listener. Preferred method of binding to events.
+     */
+    this.addEventListener = function(event, callback) {
+        if (!(event in listeners)) {
+            listeners[event] = [];
+        }
+        // Currently allows duplicate callbacks. Should it?
+        listeners[event].push(callback);
+    };
+
+    /**
+     * Remove an event callback that has already been bound.
+     * Only works on the matching funciton, cannot be a copy.
+     */
+    this.removeEventListener = function(event, callback) {
+        if (event in listeners) {
+            // Filter will return a new array with the callback removed
+            listeners[event] = listeners[event].filter(function(ev) {
+                return ev !== callback;
+            });
+        }
+    };
+
+    /**
+     * Dispatch any events, including both "on" methods and events attached using addEventListener.
+     */
+    this.dispatchEvent = function(event) {
+        if (typeof self["on" + event] === "function") {
+            self["on" + event]();
+        }
+        if (event in listeners) {
+            for (var i = 0, len = listeners[event].length; i < len; i++) {
+                listeners[event][i].call(self);
+            }
+        }
+    };
+
+    /**
+     * Changes readyState and calls onreadystatechange.
+     *
+     * @param int state New state
+     */
+    var setState = function(state) {
+        if (state == self.LOADING || self.readyState !== state) {
+            self.readyState = state;
+
+            if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) {
+                self.dispatchEvent("readystatechange");
+            }
+
+            if (self.readyState === self.DONE && !errorFlag) {
+                self.dispatchEvent("load");
+                // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie)
+                self.dispatchEvent("loadend");
+            }
+        }
+    };
+};

+ 2 - 20
views/main/hexo-image.js

@@ -1,22 +1,3 @@
-/*
- *  This file is part of Moeditor.
- *
- *  Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
- *
- *  Moeditor is free software: you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation, either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  Moeditor is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with Moeditor. If not, see <http://www.gnu.org/licenses/>.
- */
-
 window.md5 = (text) => {
     return require('crypto').createHash('md5').update(text).digest('hex');
 };
@@ -102,7 +83,8 @@ class ImgManager {
 
     getQiNiuServer() {
         if (!this.qiniuServer) {
-            this.qiniuServer = new (require('./hexo-qiniu'))();
+            // this.qiniuServer = new (require('./hexo-qiniu'))();
+            this.qiniuServer = moeApp.qiniuServer;
             this.qiniuServer.update(
                 moeApp.config.get('image-qiniu-accessKey'),
                 moeApp.config.get('image-qiniu-secretKey'),

+ 1 - 1
views/main/moe-contextmenu.js

@@ -115,7 +115,7 @@ document.addEventListener('DOMContentLoaded', () => {
                     type: 'separator',
                 },
                 {
-                    label: '一键上传',
+                    label: (moeApp.config.get('image-web-type')=='qiniu')? __('UploadToQiNiu'): __('UploadToSMMS'),
                     enabled: !imgManager.isUploading,
                     click(item, hexoWindow) {
                         !imgManager.uploadLocalSrc();

+ 0 - 48
views/main/qiniu/qiniu/rpc.js

@@ -1,48 +0,0 @@
-var urllib = require('urllib');
-var util = require('./util');
-var conf = require('./conf');
-
-exports.post = post;
-exports.postMultipart = postMultipart;
-exports.postWithForm = postWithForm;
-exports.postWithoutForm = postWithoutForm;
-
-function postMultipart(requestURI, requestForm, callbackFunc) {
-  return post(requestURI, requestForm, callbackFunc);
-}
-
-function postWithForm(requestURI, requestForm, token, callbackFunc) {
-  var headers = {
-    'Content-Type': 'application/x-www-form-urlencoded'
-  };
-  if (token) {
-    headers['Authorization'] = token;
-  }
-  return post(requestURI, requestForm, headers, callbackFunc);
-}
-
-function postWithoutForm(requestURI, token, callbackFunc) {
-  var headers = {
-    'Content-Type': 'application/x-www-form-urlencoded',
-  };
-  if (token) {
-    headers['Authorization'] = token;
-  }
-  return post(requestURI, null, headers, callbackFunc);
-}
-
-function post(requestURI, data, callbackFunc) {
-    let xhr = new XMLHttpRequest();
-    xhr.open('post', requestURI);
-    xhr.onreadystatechange = () => {
-        if (xhr.readyState === 4) {
-            if (typeof callbackFunc === "function") {
-                callbackFunc(xhr.status, xhr.response, xhr.responseText);
-            }
-        }
-    }
-    // xhr.upload.onprogress = (e) => {
-    //     console.log(Math.floor(100 * e.loaded / e.total) + '%');
-    // }
-    xhr.send(data)
-}

+ 0 - 230
views/main/qiniu/qiniu/storage/form.js

@@ -1,230 +0,0 @@
-const conf = require('../conf');
-const util = require('../util');
-const rpc = require('../rpc');
-const fs = require('fs');
-const getCrc32 = require('crc32');
-const path = require('path');
-const mime = require('mime');
-const Readable = require('stream').Readable;
-const formstream = require('formstream');
-const zone = require('../zone');
-const digest = require('../auth/digest');
-
-exports.FormUploader = FormUploader;
-exports.PutExtra = PutExtra;
-
-function FormUploader(config) {
-  this.config = config || new conf.Config();
-}
-
-// 上传可选参数
-// @params fname   请求体中的文件的名称
-// @params params  额外参数设置,参数名称必须以x:开头
-// @param mimeType 指定文件的mimeType
-// @param crc32    指定文件的crc32值
-// @param checkCrc 指定是否检测文件的crc32值
-function PutExtra(fname, params, mimeType, crc32, checkCrc) {
-  this.fname = fname || '';
-  this.params = params || {};
-  this.mimeType = mimeType || null;
-  this.crc32 = crc32 || null;
-  this.checkCrc = checkCrc || 1;
-}
-
-FormUploader.prototype.putStream = function(uploadToken, key, fsStream,
-  putExtra, callbackFunc) {
-  putExtra = putExtra || new PutExtra();
-  if (!putExtra.mimeType) {
-    putExtra.mimeType = 'application/octet-stream';
-  }
-
-  if (!putExtra.fname) {
-    putExtra.fname = key ? key : 'fname';
-  }
-
-  fsStream.on("error", function(err) {
-    //callbackFunc
-    callbackFunc(err, null, null);
-    return;
-  });
-
-  var useCache = false;
-  var that = this;
-  if (this.config.zone) {
-    if (this.config.zoneExpire == -1) {
-      useCache = true;
-    } else {
-      if (!util.isTimestampExpired(this.config.zoneExpire)) {
-        useCache = true;
-      }
-    }
-  }
-
-  var accessKey = util.getAKFromUptoken(uploadToken);
-  var bucket = util.getBucketFromUptoken(uploadToken);
-  if (useCache) {
-    createMultipartForm(uploadToken, key, fsStream, putExtra, function(
-      postForm) {
-      putReq(that.config, postForm, callbackFunc);
-    });
-  } else {
-    zone.getZoneInfo(accessKey, bucket, function(err, cZoneInfo,
-      cZoneExpire) {
-      if (err) {
-        callbackFunc(err, null, null);
-        return;
-      }
-
-      //update object
-      that.config.zone = cZoneInfo;
-      that.config.zoneExpire = cZoneExpire;
-
-      //req
-      createMultipartForm(uploadToken, key, fsStream,
-        putExtra,
-        function(postForm) {
-          putReq(that.config, postForm, callbackFunc);
-        });
-    });
-  }
-}
-
-function putReq(config, postForm, callbackFunc) {
-  //set up hosts order
-  var upHosts = [];
-
-  if (config.useCdnDomain) {
-    if (config.zone.cdnUpHosts) {
-      config.zone.cdnUpHosts.forEach(function(host) {
-        upHosts.push(host);
-      });
-    }
-    config.zone.srcUpHosts.forEach(function(host) {
-      upHosts.push(host);
-    });
-  } else {
-    config.zone.srcUpHosts.forEach(function(host) {
-      upHosts.push(host);
-    });
-    config.zone.cdnUpHosts.forEach(function(host) {
-      upHosts.push(host);
-    });
-  }
-
-  var scheme = config.useHttpsDomain ? "https://" : "http://";
-  var upDomain = scheme + upHosts[0];
-  rpc.postMultipart(upDomain, postForm, callbackFunc);
-}
-
-// 上传字节
-//
-FormUploader.prototype.put = function(uploadToken, key, body, putExtra,
-  callbackFunc) {
-  var fsStream = new Readable();
-  fsStream.push(body);
-  fsStream.push(null);
-
-  putExtra = putExtra || new PutExtra();
-  return this.putStream(uploadToken, key, fsStream, putExtra, callbackFunc)
-}
-
-FormUploader.prototype.putWithoutKey = function(uploadToken, body, putExtra,
-  callbackFunc) {
-  return this.put(uploadToken, null, body, putExtra, callbackFunc);
-}
-
-function createMultipartForm(uploadToken, key, fsStream, putExtra, callbackFunc) {
-    var formdata = new FormData();
-    formdata.append('token', uploadToken);
-    formdata.append('key', key);
-    formdata.append('file', fsStream);
-    for (var k in putExtra.params) {
-        if (k.startsWith("x:")) {
-            formdata.append(k, putExtra.params[k].toString());
-        }
-    }
-
-    callbackFunc(formdata);
-
-    return;
-
-    let xhr = new XMLHttpRequest();
-    xhr.open('post', 'https://sm.ms/api/upload');
-    xhr.onreadystatechange = () => {
-        if (xhr.readyState === 4) { // 成功完成
-            // 判断响应结果:
-            if (xhr.status === 200) {
-                // 成功,通过responseText拿到响应的文本:
-                if (typeof callback === "function") {
-                    callback(imgPath, JSON.parse(xhr.responseText))
-                }
-            } else {
-                // 失败,根据响应码判断失败原因:
-                if (typeof callback === "function") {
-                    callback(imgPath, {code: xhr.status})
-                }
-            }
-            console.log(xhr.responseText)
-        }
-    }
-    // xhr.upload.onprogress = (e) => {
-    //     console.log(Math.floor(100 * e.loaded / e.total) + '%');
-    // }
-    xhr.send(formdata);
-
-    return;
-
-    var postForm = formstream();
-  postForm.field('token', uploadToken);
-  if (key) {
-    postForm.field('key', key);
-  }
-
-  postForm.stream('file', fsStream, putExtra.fname, putExtra.mimeType);
-
-  //putExtra params
-  for (var k in putExtra.params) {
-    if (k.startsWith("x:")) {
-      postForm.field(k, putExtra.params[k].toString());
-    }
-  }
-  var fileBody = [];
-  fsStream.on('data', function(data) {
-    fileBody.push(data);
-  });
-
-  fsStream.on('end', function() {
-    fileBody = Buffer.concat(fileBody);
-    var bodyCrc32 = parseInt("0x" + getCrc32(fileBody));
-    postForm.field('crc32', bodyCrc32);
-  });
-  callbackFunc(postForm);
-}
-
-
-// 上传本地文件
-// @params uploadToken 上传凭证
-// @param key 目标文件名
-// @param localFile 本地文件路径
-// @param putExtra 额外选项
-// @param callbackFunc 回调函数
-FormUploader.prototype.putFile = function(uploadToken, key, localFile, putExtra,
-  callbackFunc) {
-  putExtra = putExtra || new PutExtra();
-  var fsStream = fs.createReadStream(localFile);
-
-  if (!putExtra.mimeType) {
-    putExtra.mimeType = mime.lookup(localFile);
-  }
-
-  if (!putExtra.fname) {
-    putExtra.fname = path.basename(localFile);
-  }
-
-  return this.putStream(uploadToken, key, fsStream, putExtra, callbackFunc);
-}
-
-FormUploader.prototype.putFileWithoutKey = function(uploadToken, localFile,
-  putExtra, callbackFunc) {
-  return this.putFile(uploadToken, null, localFile, putExtra, callbackFunc);
-}

+ 50 - 31
views/settings/moe-settings.js

@@ -223,7 +223,7 @@ document.addEventListener('DOMContentLoaded', () => {
         }, (fileName) => {
             if (!fileName || fileName.length === 0) return;
             fileName = fileName[0];
-            if (hexoConfigInput.value !== fileName){
+            if (hexoConfigInput.value !== fileName) {
                 moeApp.config.set('hexo-config', fileName);
                 ipcRenderer.send('setting-changed', {key: 'hexo-config', val: fileName});
                 hexoConfigInput.value = fileName;
@@ -470,7 +470,7 @@ document.addEventListener('DOMContentLoaded', () => {
         }, (fileName) => {
             if (!fileName || fileName.length === 0) return;
             fileName = fileName[0];
-            if (imageSourceInput.value !== fileName){
+            if (imageSourceInput.value !== fileName) {
                 moeApp.config.set(imageSourceInput.id, fileName);
                 ipcRenderer.send('setting-changed', {key: imageSourceInput.id, val: fileName});
                 imageSourceInput.value = fileName;
@@ -489,7 +489,19 @@ document.addEventListener('DOMContentLoaded', () => {
 
     function hasQiNiuServer() {
         if (imageAccessKey.value && imageSecretKey.value && !global.qiniuServer) {
-            global.qiniuServer = new (require('../main/hexo-qiniu'))(imageAccessKey.value, imageSecretKey.value)
+            global.qiniuServer = moeApp.qiniuServer;
+            qiniuServer.update(
+                moeApp.config.get('image-qiniu-accessKey'),
+                moeApp.config.get('image-qiniu-secretKey'),
+                moeApp.config.get('image-qiniu-bucket'),
+                moeApp.config.get('image-qiniu-url-protocol') + moeApp.config.get('image-qiniu-url') + '/'
+            );
+            qiniuServer.update(
+                imageAccessKey.value,
+                imageSecretKey.value,
+                imageBucket.value,
+                imageBaseWebProtocol + imageBaseWeb + '/'
+            )
             return true;
         }
         return global.qiniuServer;
@@ -511,7 +523,7 @@ document.addEventListener('DOMContentLoaded', () => {
                             imageBucket.value = option.value;
                         }
                     })
-                    if (!imageBucket.value && imageBucket.firstChild){
+                    if (!imageBucket.value && imageBucket.firstChild) {
                         imageBucket.value = imageBucket.firstChild.value;
                     }
                     let event = new Event('change');
@@ -521,10 +533,11 @@ document.addEventListener('DOMContentLoaded', () => {
             })
         }
     }
+
     function checkURLs(bucket) {
         if (hasQiNiuServer()) {
             let oldurl = moeApp.config.get(imageBaseWeb.id);
-            qiniuServer.getBucketsUrl(bucket,(response) => {
+            qiniuServer.getBucketsUrl(bucket, (response) => {
                 if (response.code == 200) {
                     imageBaseWeb.innerHTML = '';
                     response.data.forEach((url) => {
@@ -537,7 +550,7 @@ document.addEventListener('DOMContentLoaded', () => {
                             imageBaseWeb.value = oldurl;
                         }
                     })
-                    if (!imageBaseWeb.value && imageBaseWeb.firstChild){
+                    if (!imageBaseWeb.value && imageBaseWeb.firstChild) {
                         imageBaseWeb.value = imageBaseWeb.firstChild.value;
                     }
                     let event = new Event('change');
@@ -567,34 +580,39 @@ document.addEventListener('DOMContentLoaded', () => {
         imageTabItem.click();
     })
 
-    imageAccessKey.addEventListener('blur',()=>{
+    imageAccessKey.addEventListener('blur', () => {
         imageAccessKey.type = 'password';
-        if (imageAccessKey.value && imageAccessKey.value == 40 && imageSecretKey.value && imageSecretKey.value == 40){
-            if (hasQiNiuServer()){
-                qiniuServer.update(imageAccessKey.value,imageSecretKey.value)
-                ipcRenderer.send('setting-changed', {key: imageAccessKey.id, val: imageAccessKey.value});
-
+        if (imageAccessKey.value && imageAccessKey.value.length == 40 && imageSecretKey.value && imageSecretKey.value.length == 40) {
+            let oldKey = moeApp.config.get(imageAccessKey.id);
+            if (oldKey != imageAccessKey.value) {
+                moeApp.config.set(imageAccessKey.id, imageAccessKey.value);
+                if (hasQiNiuServer()) {
+                    qiniuServer.update(imageAccessKey.value, imageSecretKey.value)
+                    ipcRenderer.send('setting-changed', {key: imageAccessKey.id, val: imageAccessKey.value});
+                    checkBuckets();
+                }
             }
-            let event = new Event('change');
-            imageBucket.dispatchEvent(event);
         }
     })
-    imageAccessKey.addEventListener('focus',()=>{
+    imageAccessKey.addEventListener('focus', () => {
         imageAccessKey.type = 'type';
     })
-    imageSecretKey.addEventListener('blur',()=>{
+    imageSecretKey.addEventListener('blur', () => {
         imageSecretKey.type = 'password';
-        if (imageAccessKey.value && imageAccessKey.value == 40 && imageSecretKey.value && imageSecretKey.value == 40){
-            if (hasQiNiuServer()){
-                qiniuServer.update(imageAccessKey.value,imageSecretKey.value)
-                ipcRenderer.send('setting-changed', {key: imageSecretKey.id, val: imageSecretKey.value});
+        if (imageAccessKey.value && imageAccessKey.value.length == 40 && imageSecretKey.value && imageSecretKey.value.length == 40) {
+            let oldKey = moeApp.config.get(imageSecretKey.id);
+            if (oldKey != imageSecretKey.value) {
+                moeApp.config.set(imageSecretKey.id, imageSecretKey.value);
+                if (hasQiNiuServer()) {
+                    qiniuServer.update(imageAccessKey.value, imageSecretKey.value)
+                    ipcRenderer.send('setting-changed', {key: imageSecretKey.id, val: imageSecretKey.value});
+                    checkBuckets();
+                }
             }
-            let event = new Event('change');
-            imageBucket.dispatchEvent(event);
         }
     })
 
-    imageSecretKey.addEventListener('focus',()=>{
+    imageSecretKey.addEventListener('focus', () => {
         imageSecretKey.type = 'type';
     })
 
@@ -605,8 +623,8 @@ document.addEventListener('DOMContentLoaded', () => {
     })
 
 
-    function protocolChange(){
-        if (imageBaseWebProtocol.value == 'http://'){
+    function protocolChange(type) {
+        if ((!type && imageBaseWebProtocol.value == 'http://') ||  type == 'https://') {
             imageBaseWebProtocol.style.width = '53px';
             imageBaseWeb.style.width = 'calc(100% - 57px)';
             imageBaseWebProtocol.value = 'https://';
@@ -616,26 +634,27 @@ document.addEventListener('DOMContentLoaded', () => {
             imageBaseWebProtocol.value = 'http://';
         }
     }
+
     imageBaseWebProtocol.value = moeApp.config.get('image-qiniu-url-protocol');
-    protocolChange();
-    imageBaseWebProtocol.addEventListener('click',()=>{
+    protocolChange(imageBaseWebProtocol.value);
+    imageBaseWebProtocol.addEventListener('click', () => {
         protocolChange();
         imageBaseWeb.dispatchEvent(new Event('change'));
     })
 
     imageBaseWeb.addEventListener('change', function (e) {
         let protocol = moeApp.config.get(imageBaseWebProtocol.id)
-        let oldURL =  moeApp.config.get(imageBaseWeb.id);
+        let oldURL = moeApp.config.get(imageBaseWeb.id);
         let value = {
-            oldURL: (oldURL ? protocol + oldURL: ''),
-            newURL: (imageBaseWeb.value ? imageBaseWebProtocol.value + imageBaseWeb.value: '')
+            oldURL: (oldURL ? protocol + oldURL : ''),
+            newURL: (imageBaseWeb.value ? imageBaseWebProtocol.value + imageBaseWeb.value : '')
         };
         moeApp.config.set(imageBaseWebProtocol.id, imageBaseWebProtocol.value);
         moeApp.config.set(imageBaseWeb.id, imageBaseWeb.value);
         ipcRenderer.send('setting-changed', {key: imageBaseWeb.id, val: value});
     })
 
-    let type =  moeApp.config.get(imageType.id);
+    let type = moeApp.config.get(imageType.id);
     if (type == 'qiniu') {
         imageType.value = type;
         let event = new Event('change');

+ 12 - 12
views/settings/settings.html

@@ -289,8 +289,8 @@
                     <div class="panel" data-tab="image" style="display: block; ">
                         <table width="80%" align="center">
                             <tr>
-                                <td width="40%" class="l10n">Source Center</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">Source Center</td>
+                                <td width="70%">
                                     <input class="settings-item-image" readonly id="image-source-center" type="url" style="width: calc(100% - 25px);"/>
                                     <div style="display: inline-block; margin-left: -1px; position: absolute; " id="image-source-center-btn">
                                     <button class="button-add-remove button-add" ><i class="fa fa-folder-o" aria-hidden="true"></i></button>
@@ -299,8 +299,8 @@
                                 </td>
                             </tr>
                             <tr>
-                                <td width="40%" class="l10n">Web Type</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">Web Type</td>
+                                <td width="70%">
                                     <select class="settings-item-image" id="image-web-type">
                                         <option value="sm" class="l10n">WebSM</option>
                                         <option value="qiniu" class="l10n">QiNiu</option>
@@ -308,27 +308,27 @@
                                 </td>
                             </tr>
                             <tr data-type="image-qiniu" style="display:none">
-                                <td width="40%" class="l10n">AccessKey</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">AccessKey</td>
+                                <td width="70%">
                                     <input class="settings-item-image" type="password" id="image-qiniu-accessKey"/>
                                 </td>
                             </tr>
                             <tr data-type="image-qiniu" style="display:none">
-                                <td width="40%" class="l10n">SecretKey</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">SecretKey</td>
+                                <td width="70%">
                                     <input class="settings-item-image" type="password" id="image-qiniu-secretKey"/>
                                 </td>
                             </tr>
                             <tr data-type="image-qiniu" style="display:none">
-                                <td width="40%" class="l10n">Bucket</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">Bucket</td>
+                                <td width="70%">
                                     <select class="settings-item-image" id="image-qiniu-bucket">
                                     </select>
                                 </td>
                             </tr>
                             <tr data-type="image-qiniu" style="display:none">
-                                <td width="40%" class="l10n">BaseWeb</td>
-                                <td width="60%">
+                                <td width="30%" class="l10n">BaseWeb</td>
+                                <td width="70%">
                                     <input class="settings-item-image" id="image-qiniu-url-protocol" readonly style="transition: width .5s;  width:53px" />
                                     <select class="settings-item-image" id="image-qiniu-url" style="transition: width .5s;width: calc(100% - 54px);">
                                     </select>