pinme 2.0.0-beta.10 → 2.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +907 -912
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1775,7 +1775,7 @@ var import_chalk22 = __toESM(require("chalk"));
|
|
|
1775
1775
|
var import_figlet5 = __toESM(require("figlet"));
|
|
1776
1776
|
|
|
1777
1777
|
// package.json
|
|
1778
|
-
var version = "2.0.0-beta.
|
|
1778
|
+
var version = "2.0.0-beta.11";
|
|
1779
1779
|
|
|
1780
1780
|
// bin/upload.ts
|
|
1781
1781
|
var import_path7 = __toESM(require("path"));
|
|
@@ -5561,11 +5561,11 @@ var {
|
|
|
5561
5561
|
} = axios_default;
|
|
5562
5562
|
|
|
5563
5563
|
// bin/utils/uploadToIpfsSplit.ts
|
|
5564
|
-
var
|
|
5565
|
-
var
|
|
5564
|
+
var import_fs_extra5 = __toESM(require("fs-extra"));
|
|
5565
|
+
var import_path6 = __toESM(require("path"));
|
|
5566
5566
|
var import_form_data2 = __toESM(require("form-data"));
|
|
5567
5567
|
var import_ora = __toESM(require("ora"));
|
|
5568
|
-
var
|
|
5568
|
+
var crypto3 = __toESM(require("crypto"));
|
|
5569
5569
|
|
|
5570
5570
|
// bin/utils/uploadLimits.ts
|
|
5571
5571
|
var import_fs = __toESM(require("fs"));
|
|
@@ -5746,713 +5746,177 @@ function getUid() {
|
|
|
5746
5746
|
return getDeviceId();
|
|
5747
5747
|
}
|
|
5748
5748
|
|
|
5749
|
-
// bin/utils/
|
|
5750
|
-
var
|
|
5751
|
-
var
|
|
5752
|
-
var
|
|
5753
|
-
var
|
|
5754
|
-
var
|
|
5755
|
-
var
|
|
5756
|
-
var
|
|
5757
|
-
var
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
simulationStartTime = 0;
|
|
5768
|
-
constructor(fileName, isDirectory = false) {
|
|
5769
|
-
this.fileName = fileName;
|
|
5770
|
-
this.spinner = (0, import_ora.default)(`Preparing to upload ${fileName}...`).start();
|
|
5771
|
-
this.startTime = Date.now();
|
|
5772
|
-
this.stepStartTime = Date.now();
|
|
5773
|
-
this.startProgress();
|
|
5774
|
-
}
|
|
5775
|
-
startStep(stepIndex, stepName) {
|
|
5776
|
-
this.currentStep = stepIndex;
|
|
5777
|
-
this.stepStartTime = Date.now();
|
|
5778
|
-
}
|
|
5779
|
-
updateProgress(progress, total) {
|
|
5780
|
-
}
|
|
5781
|
-
completeStep() {
|
|
5749
|
+
// bin/utils/webLogin.ts
|
|
5750
|
+
var import_crypto2 = __toESM(require("crypto"));
|
|
5751
|
+
var import_http4 = __toESM(require("http"));
|
|
5752
|
+
var import_url2 = require("url");
|
|
5753
|
+
var import_chalk3 = __toESM(require("chalk"));
|
|
5754
|
+
var import_child_process = require("child_process");
|
|
5755
|
+
var import_fs_extra4 = __toESM(require("fs-extra"));
|
|
5756
|
+
var import_os4 = __toESM(require("os"));
|
|
5757
|
+
var import_path5 = __toESM(require("path"));
|
|
5758
|
+
function openBrowser(url2) {
|
|
5759
|
+
const platform = process.platform;
|
|
5760
|
+
let command;
|
|
5761
|
+
if (platform === "darwin") {
|
|
5762
|
+
command = `open "${url2}"`;
|
|
5763
|
+
} else if (platform === "win32") {
|
|
5764
|
+
command = `start "" "${url2}"`;
|
|
5765
|
+
} else {
|
|
5766
|
+
command = `xdg-open "${url2}"`;
|
|
5782
5767
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5768
|
+
(0, import_child_process.exec)(command, (err) => {
|
|
5769
|
+
if (err) {
|
|
5770
|
+
console.log(import_chalk3.default.yellow(`Unable to open browser automatically. Please visit manually: ${url2}`));
|
|
5771
|
+
}
|
|
5772
|
+
});
|
|
5773
|
+
}
|
|
5774
|
+
var CONFIG_DIR2 = import_path5.default.join(import_os4.default.homedir(), ".pinme");
|
|
5775
|
+
var AUTH_FILE2 = import_path5.default.join(CONFIG_DIR2, "auth.json");
|
|
5776
|
+
var DEFAULT_OPTIONS = {
|
|
5777
|
+
apiBaseUrl: "https://pinme.benny1996.win/api/v4",
|
|
5778
|
+
webBaseUrl: process.env.PINME_WEB_URL || "http://localhost:5173",
|
|
5779
|
+
callbackPort: 34567,
|
|
5780
|
+
callbackPath: "/cli/callback"
|
|
5781
|
+
};
|
|
5782
|
+
var WebLoginManager = class {
|
|
5783
|
+
config;
|
|
5784
|
+
server = null;
|
|
5785
|
+
resolvePromise = null;
|
|
5786
|
+
rejectPromise = null;
|
|
5787
|
+
loginToken = "";
|
|
5788
|
+
constructor(options = {}) {
|
|
5789
|
+
this.config = { ...DEFAULT_OPTIONS, ...options };
|
|
5787
5790
|
}
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
this.
|
|
5791
|
+
async login() {
|
|
5792
|
+
console.log(import_chalk3.default.blue("Starting login flow...\n"));
|
|
5793
|
+
this.loginToken = this.generateLoginToken();
|
|
5794
|
+
console.log(import_chalk3.default.blue("Starting local callback server..."));
|
|
5795
|
+
await this.startCallbackServer();
|
|
5796
|
+
try {
|
|
5797
|
+
const loginUrl = this.buildLoginUrl();
|
|
5798
|
+
console.log(import_chalk3.default.blue("Opening browser..."));
|
|
5799
|
+
console.log(import_chalk3.default.white("If browser does not open automatically, please visit manually:"));
|
|
5800
|
+
console.log(import_chalk3.default.cyan(` ${loginUrl}
|
|
5801
|
+
`));
|
|
5802
|
+
openBrowser(loginUrl);
|
|
5803
|
+
console.log(import_chalk3.default.yellow("Please complete login in browser..."));
|
|
5804
|
+
console.log(import_chalk3.default.gray("Browser will close automatically after successful login.\n"));
|
|
5805
|
+
const authToken = await this.waitForCallback();
|
|
5806
|
+
const authConfig = this.parseAuthToken(authToken);
|
|
5807
|
+
this.saveAuthConfig(authConfig);
|
|
5808
|
+
console.log(import_chalk3.default.green("\nLogin successful!"));
|
|
5809
|
+
if (authConfig.email) {
|
|
5810
|
+
console.log(import_chalk3.default.green(`Welcome, ${authConfig.email}`));
|
|
5811
|
+
}
|
|
5812
|
+
console.log(import_chalk3.default.gray(`Address: ${authConfig.address}`));
|
|
5813
|
+
return authConfig;
|
|
5814
|
+
} catch (error) {
|
|
5815
|
+
console.error(import_chalk3.default.red(`
|
|
5816
|
+
Login failed: ${error.message}`));
|
|
5817
|
+
throw error;
|
|
5818
|
+
} finally {
|
|
5819
|
+
this.closeServer();
|
|
5820
|
+
}
|
|
5791
5821
|
}
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
this.spinner.fail(`Upload failed: ${error}`);
|
|
5822
|
+
generateLoginToken() {
|
|
5823
|
+
return import_crypto2.default.randomBytes(32).toString("hex");
|
|
5795
5824
|
}
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5825
|
+
async startCallbackServer() {
|
|
5826
|
+
return new Promise((resolve, reject) => {
|
|
5827
|
+
this.server = import_http4.default.createServer(async (req, res) => {
|
|
5828
|
+
try {
|
|
5829
|
+
const url2 = new import_url2.URL(req.url || "", `http://localhost:${this.config.callbackPort}`);
|
|
5830
|
+
if (url2.pathname === this.config.callbackPath) {
|
|
5831
|
+
const authToken = url2.searchParams.get("token");
|
|
5832
|
+
const error = url2.searchParams.get("error");
|
|
5833
|
+
const loginToken = url2.searchParams.get("login_token");
|
|
5834
|
+
if (error) {
|
|
5835
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5836
|
+
res.end(this.getErrorHtml(error));
|
|
5837
|
+
if (this.rejectPromise) {
|
|
5838
|
+
this.rejectPromise(new Error(error));
|
|
5839
|
+
}
|
|
5840
|
+
return;
|
|
5841
|
+
}
|
|
5842
|
+
if (!authToken) {
|
|
5843
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5844
|
+
res.end(this.getErrorHtml("Auth token not received"));
|
|
5845
|
+
if (this.rejectPromise) {
|
|
5846
|
+
this.rejectPromise(new Error("Auth token not received"));
|
|
5847
|
+
}
|
|
5848
|
+
return;
|
|
5849
|
+
}
|
|
5850
|
+
if (loginToken !== this.loginToken) {
|
|
5851
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5852
|
+
res.end(this.getErrorHtml("Login token invalid or expired"));
|
|
5853
|
+
if (this.rejectPromise) {
|
|
5854
|
+
this.rejectPromise(new Error("Login token invalid or expired"));
|
|
5855
|
+
}
|
|
5856
|
+
return;
|
|
5857
|
+
}
|
|
5858
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
5859
|
+
res.end(this.getSuccessHtml());
|
|
5860
|
+
if (this.resolvePromise) {
|
|
5861
|
+
this.resolvePromise(authToken);
|
|
5862
|
+
}
|
|
5863
|
+
} else {
|
|
5864
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
5865
|
+
res.end("Not Found");
|
|
5866
|
+
}
|
|
5867
|
+
} catch (err) {
|
|
5868
|
+
console.error("Callback error:", err);
|
|
5869
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
5870
|
+
res.end("Internal Server Error");
|
|
5871
|
+
}
|
|
5872
|
+
});
|
|
5873
|
+
this.server.on("error", (err) => {
|
|
5874
|
+
reject(err);
|
|
5875
|
+
});
|
|
5876
|
+
this.server.listen(this.config.callbackPort, "127.0.0.1", () => {
|
|
5877
|
+
console.log(import_chalk3.default.gray(`Local server: http://localhost:${this.config.callbackPort}`));
|
|
5878
|
+
resolve();
|
|
5879
|
+
});
|
|
5880
|
+
setTimeout(() => {
|
|
5881
|
+
if (this.rejectPromise) {
|
|
5882
|
+
this.rejectPromise(new Error("Login timeout, please try again"));
|
|
5883
|
+
}
|
|
5884
|
+
this.closeServer();
|
|
5885
|
+
}, 10 * 60 * 1e3);
|
|
5886
|
+
});
|
|
5803
5887
|
}
|
|
5804
|
-
|
|
5805
|
-
this.
|
|
5806
|
-
const
|
|
5807
|
-
|
|
5888
|
+
buildLoginUrl() {
|
|
5889
|
+
const callbackUrl = `http://localhost:${this.config.callbackPort}${this.config.callbackPath}`;
|
|
5890
|
+
const url2 = `${this.config.webBaseUrl}/#/cli-login?login_token=${this.loginToken}&callback_url=${encodeURIComponent(callbackUrl)}`;
|
|
5891
|
+
return url2;
|
|
5808
5892
|
}
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
const simulationElapsed = Date.now() - this.simulationStartTime;
|
|
5815
|
-
const simulationProgress = Math.min(simulationElapsed / 6e4, 1);
|
|
5816
|
-
progress = 0.9 + simulationProgress * 0.09;
|
|
5817
|
-
} else {
|
|
5818
|
-
progress = this.calculateProgress(elapsed);
|
|
5819
|
-
}
|
|
5820
|
-
const duration = this.formatDuration(Math.floor(elapsed / 1e3));
|
|
5821
|
-
const progressBar = this.createProgressBar(progress);
|
|
5822
|
-
this.spinner.text = `Uploading ${this.fileName}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
|
|
5823
|
-
}, PROGRESS_UPDATE_INTERVAL);
|
|
5893
|
+
waitForCallback() {
|
|
5894
|
+
return new Promise((resolve, reject) => {
|
|
5895
|
+
this.resolvePromise = resolve;
|
|
5896
|
+
this.rejectPromise = reject;
|
|
5897
|
+
});
|
|
5824
5898
|
}
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5899
|
+
parseAuthToken(authToken) {
|
|
5900
|
+
const firstDash = authToken.indexOf("-");
|
|
5901
|
+
if (firstDash <= 0 || firstDash === authToken.length - 1) {
|
|
5902
|
+
throw new Error("Invalid token format");
|
|
5829
5903
|
}
|
|
5904
|
+
const address = authToken.slice(0, firstDash).trim();
|
|
5905
|
+
const token = authToken.slice(firstDash + 1).trim();
|
|
5906
|
+
if (!address || !token) {
|
|
5907
|
+
throw new Error("Token parsing failed: address or token is empty");
|
|
5908
|
+
}
|
|
5909
|
+
return { address, token };
|
|
5830
5910
|
}
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
);
|
|
5836
|
-
}
|
|
5837
|
-
createProgressBar(progress, width = 20) {
|
|
5838
|
-
const percentage = Math.min(progress, 1);
|
|
5839
|
-
const filledWidth = Math.round(width * percentage);
|
|
5840
|
-
const emptyWidth = width - filledWidth;
|
|
5841
|
-
return `[${"\u2588".repeat(filledWidth)}${"\u2591".repeat(emptyWidth)}]`;
|
|
5911
|
+
saveAuthConfig(config) {
|
|
5912
|
+
import_fs_extra4.default.ensureDirSync(CONFIG_DIR2);
|
|
5913
|
+
import_fs_extra4.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
|
|
5914
|
+
import_fs_extra4.default.chmodSync(AUTH_FILE2, 384);
|
|
5842
5915
|
}
|
|
5843
|
-
|
|
5844
|
-
if (
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
const minutes = Math.floor(seconds / 60);
|
|
5848
|
-
const remainingSeconds = seconds % 60;
|
|
5849
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
5850
|
-
} else {
|
|
5851
|
-
const hours = Math.floor(seconds / 3600);
|
|
5852
|
-
const minutes = Math.floor(seconds % 3600 / 60);
|
|
5853
|
-
const remainingSeconds = seconds % 60;
|
|
5854
|
-
return `${hours}h ${minutes}m ${remainingSeconds}s`;
|
|
5855
|
-
}
|
|
5856
|
-
}
|
|
5857
|
-
};
|
|
5858
|
-
async function calculateMD5(filePath) {
|
|
5859
|
-
return new Promise((resolve, reject) => {
|
|
5860
|
-
const hash = crypto2.createHash("md5");
|
|
5861
|
-
const stream4 = import_fs_extra4.default.createReadStream(filePath);
|
|
5862
|
-
stream4.on("data", hash.update.bind(hash));
|
|
5863
|
-
stream4.on("end", () => resolve(hash.digest("hex")));
|
|
5864
|
-
stream4.on("error", reject);
|
|
5865
|
-
});
|
|
5866
|
-
}
|
|
5867
|
-
async function compressDirectory(sourcePath) {
|
|
5868
|
-
return new Promise((resolve, reject) => {
|
|
5869
|
-
const tempDir = require("os").tmpdir();
|
|
5870
|
-
if (!import_fs_extra4.default.existsSync(tempDir)) {
|
|
5871
|
-
import_fs_extra4.default.mkdirSync(tempDir, { recursive: true });
|
|
5872
|
-
}
|
|
5873
|
-
const outputPath = import_path5.default.join(
|
|
5874
|
-
tempDir,
|
|
5875
|
-
`pinme_${import_path5.default.basename(sourcePath)}_${Date.now()}.zip`
|
|
5876
|
-
);
|
|
5877
|
-
const output = import_fs_extra4.default.createWriteStream(outputPath);
|
|
5878
|
-
const zlib2 = require("zlib");
|
|
5879
|
-
const gzip = zlib2.createGzip({ level: 9 });
|
|
5880
|
-
output.on("close", () => resolve(outputPath));
|
|
5881
|
-
gzip.on("error", reject);
|
|
5882
|
-
gzip.pipe(output);
|
|
5883
|
-
const stats = import_fs_extra4.default.statSync(sourcePath);
|
|
5884
|
-
if (stats.isDirectory()) {
|
|
5885
|
-
const archive = require("archiver");
|
|
5886
|
-
const archiveStream = archive("zip", { zlib: { level: 9 } });
|
|
5887
|
-
archiveStream.on("error", reject);
|
|
5888
|
-
archiveStream.pipe(output);
|
|
5889
|
-
archiveStream.directory(sourcePath, false);
|
|
5890
|
-
archiveStream.finalize();
|
|
5891
|
-
} else {
|
|
5892
|
-
const fileStream = import_fs_extra4.default.createReadStream(sourcePath);
|
|
5893
|
-
fileStream.pipe(gzip);
|
|
5894
|
-
}
|
|
5895
|
-
});
|
|
5896
|
-
}
|
|
5897
|
-
async function initChunkSession(filePath, deviceId, isDirectory = false) {
|
|
5898
|
-
const stats = import_fs_extra4.default.statSync(filePath);
|
|
5899
|
-
const fileName = import_path5.default.basename(filePath);
|
|
5900
|
-
const fileSize = stats.size;
|
|
5901
|
-
const md5 = await calculateMD5(filePath);
|
|
5902
|
-
try {
|
|
5903
|
-
const response = await axios_default.post(
|
|
5904
|
-
`${IPFS_API_URL}/chunk/init`,
|
|
5905
|
-
{
|
|
5906
|
-
file_name: fileName,
|
|
5907
|
-
file_size: fileSize,
|
|
5908
|
-
md5,
|
|
5909
|
-
is_directory: isDirectory,
|
|
5910
|
-
uid: deviceId
|
|
5911
|
-
},
|
|
5912
|
-
{
|
|
5913
|
-
timeout: TIMEOUT,
|
|
5914
|
-
headers: { "Content-Type": "application/json" }
|
|
5915
|
-
}
|
|
5916
|
-
);
|
|
5917
|
-
const { code, msg, data } = response.data;
|
|
5918
|
-
if (code === 200 && data) {
|
|
5919
|
-
return data;
|
|
5920
|
-
}
|
|
5921
|
-
throw new Error(`Session initialization failed: ${msg} (code: ${code})`);
|
|
5922
|
-
} catch (error) {
|
|
5923
|
-
if (axios_default.isAxiosError(error)) {
|
|
5924
|
-
throw new Error(`Network error: ${error.message}`);
|
|
5925
|
-
}
|
|
5926
|
-
throw error;
|
|
5927
|
-
}
|
|
5928
|
-
}
|
|
5929
|
-
async function uploadChunkWithAbort(sessionId, chunkIndex, chunkData, deviceId, signal, retryCount = 0) {
|
|
5930
|
-
try {
|
|
5931
|
-
if (signal.aborted) {
|
|
5932
|
-
throw new Error("Request cancelled");
|
|
5933
|
-
}
|
|
5934
|
-
const form = new import_form_data2.default();
|
|
5935
|
-
form.append("session_id", sessionId);
|
|
5936
|
-
form.append("chunk_index", chunkIndex.toString());
|
|
5937
|
-
form.append("uid", deviceId);
|
|
5938
|
-
form.append("chunk", chunkData, {
|
|
5939
|
-
filename: `chunk_${chunkIndex}`,
|
|
5940
|
-
contentType: "application/octet-stream"
|
|
5941
|
-
});
|
|
5942
|
-
const response = await axios_default.post(
|
|
5943
|
-
`${IPFS_API_URL}/chunk/upload`,
|
|
5944
|
-
form,
|
|
5945
|
-
{
|
|
5946
|
-
headers: { ...form.getHeaders() },
|
|
5947
|
-
timeout: TIMEOUT,
|
|
5948
|
-
signal
|
|
5949
|
-
}
|
|
5950
|
-
);
|
|
5951
|
-
const { code, msg, data } = response.data;
|
|
5952
|
-
if (code === 200 && data) {
|
|
5953
|
-
return data;
|
|
5954
|
-
}
|
|
5955
|
-
throw new Error(`Chunk upload failed: ${msg} (code: ${code})`);
|
|
5956
|
-
} catch (error) {
|
|
5957
|
-
if (error.name === "CanceledError" || signal.aborted) {
|
|
5958
|
-
throw new Error("Request cancelled");
|
|
5959
|
-
}
|
|
5960
|
-
if (retryCount < MAX_RETRIES) {
|
|
5961
|
-
await delayWithAbortCheck(RETRY_DELAY, signal);
|
|
5962
|
-
return uploadChunkWithAbort(
|
|
5963
|
-
sessionId,
|
|
5964
|
-
chunkIndex,
|
|
5965
|
-
chunkData,
|
|
5966
|
-
deviceId,
|
|
5967
|
-
signal,
|
|
5968
|
-
retryCount + 1
|
|
5969
|
-
);
|
|
5970
|
-
}
|
|
5971
|
-
throw new Error(
|
|
5972
|
-
`Chunk ${chunkIndex + 1} upload failed after ${MAX_RETRIES} retries: ${error.message}`
|
|
5973
|
-
);
|
|
5974
|
-
}
|
|
5975
|
-
}
|
|
5976
|
-
async function delayWithAbortCheck(delay, signal) {
|
|
5977
|
-
return new Promise((resolve, reject) => {
|
|
5978
|
-
const timeoutId = setTimeout(() => {
|
|
5979
|
-
if (signal.aborted) {
|
|
5980
|
-
reject(new Error("Request cancelled"));
|
|
5981
|
-
} else {
|
|
5982
|
-
resolve();
|
|
5983
|
-
}
|
|
5984
|
-
}, delay);
|
|
5985
|
-
if (signal.aborted) {
|
|
5986
|
-
clearTimeout(timeoutId);
|
|
5987
|
-
reject(new Error("Request cancelled"));
|
|
5988
|
-
return;
|
|
5989
|
-
}
|
|
5990
|
-
const checkInterval = setInterval(() => {
|
|
5991
|
-
if (signal.aborted) {
|
|
5992
|
-
clearTimeout(timeoutId);
|
|
5993
|
-
clearInterval(checkInterval);
|
|
5994
|
-
reject(new Error("Request cancelled"));
|
|
5995
|
-
}
|
|
5996
|
-
}, 50);
|
|
5997
|
-
});
|
|
5998
|
-
}
|
|
5999
|
-
async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, deviceId, progressBar) {
|
|
6000
|
-
const fileData = import_fs_extra4.default.readFileSync(filePath);
|
|
6001
|
-
const abortController = new AbortController();
|
|
6002
|
-
let completedCount = 0;
|
|
6003
|
-
let hasFatalError = false;
|
|
6004
|
-
let fatalError = null;
|
|
6005
|
-
const uploadTasks = Array.from({ length: totalChunks }, (_, chunkIndex) => {
|
|
6006
|
-
const start = chunkIndex * chunkSize;
|
|
6007
|
-
const end = Math.min(start + chunkSize, fileData.length);
|
|
6008
|
-
const chunkData = fileData.slice(start, end);
|
|
6009
|
-
return async () => {
|
|
6010
|
-
if (abortController.signal.aborted) return;
|
|
6011
|
-
try {
|
|
6012
|
-
await uploadChunkWithAbort(
|
|
6013
|
-
sessionId,
|
|
6014
|
-
chunkIndex,
|
|
6015
|
-
chunkData,
|
|
6016
|
-
deviceId,
|
|
6017
|
-
abortController.signal
|
|
6018
|
-
);
|
|
6019
|
-
if (abortController.signal.aborted) return;
|
|
6020
|
-
completedCount++;
|
|
6021
|
-
progressBar.updateProgress(completedCount, totalChunks);
|
|
6022
|
-
} catch (error) {
|
|
6023
|
-
if (error.name === "AbortError" || abortController.signal.aborted) {
|
|
6024
|
-
return;
|
|
6025
|
-
}
|
|
6026
|
-
hasFatalError = true;
|
|
6027
|
-
fatalError = `Chunk ${chunkIndex + 1}/${totalChunks} upload failed: ${error.message}`;
|
|
6028
|
-
abortController.abort();
|
|
6029
|
-
throw new Error(fatalError);
|
|
6030
|
-
}
|
|
6031
|
-
};
|
|
6032
|
-
});
|
|
6033
|
-
try {
|
|
6034
|
-
const results = await Promise.allSettled(uploadTasks.map((task) => task()));
|
|
6035
|
-
const failedResults = results.filter(
|
|
6036
|
-
(result) => result.status === "rejected"
|
|
6037
|
-
);
|
|
6038
|
-
if (failedResults.length > 0) {
|
|
6039
|
-
const firstFailure = failedResults[0];
|
|
6040
|
-
throw new Error(
|
|
6041
|
-
firstFailure.reason.message || "Error occurred during upload"
|
|
6042
|
-
);
|
|
6043
|
-
}
|
|
6044
|
-
if (hasFatalError) {
|
|
6045
|
-
throw new Error(fatalError || "Unknown error occurred during upload");
|
|
6046
|
-
}
|
|
6047
|
-
} catch (error) {
|
|
6048
|
-
throw fatalError ? new Error(fatalError) : error;
|
|
6049
|
-
}
|
|
6050
|
-
}
|
|
6051
|
-
async function completeChunkUpload(sessionId, deviceId, importAsCar = false) {
|
|
6052
|
-
var _a;
|
|
6053
|
-
try {
|
|
6054
|
-
const requestBody = { session_id: sessionId, uid: deviceId };
|
|
6055
|
-
const projectName = (_a = process.env.PINME_PROJECT_NAME) == null ? void 0 : _a.trim();
|
|
6056
|
-
if (importAsCar) {
|
|
6057
|
-
requestBody.import_as_car = true;
|
|
6058
|
-
}
|
|
6059
|
-
if (projectName) {
|
|
6060
|
-
requestBody.project_name = projectName;
|
|
6061
|
-
}
|
|
6062
|
-
console.log(
|
|
6063
|
-
`[chunk/complete] payload=${JSON.stringify({
|
|
6064
|
-
session_id: requestBody.session_id,
|
|
6065
|
-
uid: requestBody.uid,
|
|
6066
|
-
import_as_car: !!requestBody.import_as_car,
|
|
6067
|
-
project_name: requestBody.project_name || ""
|
|
6068
|
-
})}`
|
|
6069
|
-
);
|
|
6070
|
-
const response = await axios_default.post(
|
|
6071
|
-
`${IPFS_API_URL}/chunk/complete`,
|
|
6072
|
-
requestBody,
|
|
6073
|
-
{
|
|
6074
|
-
timeout: TIMEOUT,
|
|
6075
|
-
headers: { "Content-Type": "application/json" }
|
|
6076
|
-
}
|
|
6077
|
-
);
|
|
6078
|
-
const { code, msg, data } = response.data;
|
|
6079
|
-
if (code === 200 && data) {
|
|
6080
|
-
return data.trace_id;
|
|
6081
|
-
}
|
|
6082
|
-
throw new Error(`Complete upload failed: ${msg} (code: ${code})`);
|
|
6083
|
-
} catch (error) {
|
|
6084
|
-
if (axios_default.isAxiosError(error)) {
|
|
6085
|
-
throw new Error(`Network error: ${error.message}`);
|
|
6086
|
-
}
|
|
6087
|
-
throw error;
|
|
6088
|
-
}
|
|
6089
|
-
}
|
|
6090
|
-
async function getChunkStatus(sessionId, deviceId) {
|
|
6091
|
-
try {
|
|
6092
|
-
const response = await axios_default.get(
|
|
6093
|
-
`${IPFS_API_URL}/up_status`,
|
|
6094
|
-
{
|
|
6095
|
-
params: { trace_id: sessionId, uid: deviceId },
|
|
6096
|
-
timeout: TIMEOUT,
|
|
6097
|
-
headers: { "Content-Type": "application/json" }
|
|
6098
|
-
}
|
|
6099
|
-
);
|
|
6100
|
-
const { code, msg, data } = response.data;
|
|
6101
|
-
if (code === 200) {
|
|
6102
|
-
return data;
|
|
6103
|
-
}
|
|
6104
|
-
throw new Error(`Server returned error: ${msg} (code: ${code})`);
|
|
6105
|
-
} catch (error) {
|
|
6106
|
-
if (axios_default.isAxiosError(error)) {
|
|
6107
|
-
throw new Error(`Network error: ${error.message}`);
|
|
6108
|
-
}
|
|
6109
|
-
throw error;
|
|
6110
|
-
}
|
|
6111
|
-
}
|
|
6112
|
-
async function monitorChunkProgress(traceId, deviceId, progressBar) {
|
|
6113
|
-
let consecutiveErrors = 0;
|
|
6114
|
-
const startTime = Date.now();
|
|
6115
|
-
if (progressBar) {
|
|
6116
|
-
progressBar.startSimulatingProgress();
|
|
6117
|
-
}
|
|
6118
|
-
try {
|
|
6119
|
-
while (Date.now() - startTime < MAX_POLL_TIME) {
|
|
6120
|
-
try {
|
|
6121
|
-
const status = await getChunkStatus(traceId, deviceId);
|
|
6122
|
-
consecutiveErrors = 0;
|
|
6123
|
-
if (status.is_ready && status.upload_rst.Hash) {
|
|
6124
|
-
if (progressBar) {
|
|
6125
|
-
progressBar.stopSimulatingProgress();
|
|
6126
|
-
}
|
|
6127
|
-
return {
|
|
6128
|
-
hash: status.upload_rst.Hash,
|
|
6129
|
-
shortUrl: status.upload_rst.ShortUrl
|
|
6130
|
-
};
|
|
6131
|
-
}
|
|
6132
|
-
} catch (error) {
|
|
6133
|
-
consecutiveErrors++;
|
|
6134
|
-
if (consecutiveErrors > 10) {
|
|
6135
|
-
throw new Error(`Polling failed: ${error.message}`);
|
|
6136
|
-
}
|
|
6137
|
-
}
|
|
6138
|
-
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
6139
|
-
}
|
|
6140
|
-
const maxPollTimeMinutes = Math.floor(MAX_POLL_TIME / (60 * 1e3));
|
|
6141
|
-
throw new Error(`Polling timeout after ${maxPollTimeMinutes} minutes`);
|
|
6142
|
-
} finally {
|
|
6143
|
-
if (progressBar) {
|
|
6144
|
-
progressBar.stopSimulatingProgress();
|
|
6145
|
-
}
|
|
6146
|
-
}
|
|
6147
|
-
}
|
|
6148
|
-
async function uploadDirectoryInChunks(directoryPath, deviceId, importAsCar = false) {
|
|
6149
|
-
const sizeCheck = checkDirectorySizeLimit(directoryPath);
|
|
6150
|
-
if (sizeCheck.exceeds) {
|
|
6151
|
-
throw new Error(
|
|
6152
|
-
`Directory ${directoryPath} exceeds size limit ${formatSize(
|
|
6153
|
-
sizeCheck.limit
|
|
6154
|
-
)} (size: ${formatSize(sizeCheck.size)})`
|
|
6155
|
-
);
|
|
6156
|
-
}
|
|
6157
|
-
const progressBar = new StepProgressBar(import_path5.default.basename(directoryPath), true);
|
|
6158
|
-
try {
|
|
6159
|
-
progressBar.startStep(0, "Preparing compression");
|
|
6160
|
-
const compressedPath = await compressDirectory(directoryPath);
|
|
6161
|
-
progressBar.completeStep();
|
|
6162
|
-
progressBar.startStep(1, "Initializing session");
|
|
6163
|
-
const sessionInfo = await initChunkSession(compressedPath, deviceId, true);
|
|
6164
|
-
progressBar.completeStep();
|
|
6165
|
-
progressBar.startStep(2, "Chunk upload");
|
|
6166
|
-
await uploadFileChunks(
|
|
6167
|
-
compressedPath,
|
|
6168
|
-
sessionInfo.session_id,
|
|
6169
|
-
sessionInfo.total_chunks,
|
|
6170
|
-
sessionInfo.chunk_size,
|
|
6171
|
-
deviceId,
|
|
6172
|
-
progressBar
|
|
6173
|
-
);
|
|
6174
|
-
progressBar.completeStep();
|
|
6175
|
-
progressBar.startStep(3, "Completing upload");
|
|
6176
|
-
const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
|
|
6177
|
-
progressBar.completeStep();
|
|
6178
|
-
progressBar.startStep(4, "Waiting for processing");
|
|
6179
|
-
const result = await monitorChunkProgress(traceId, deviceId, progressBar);
|
|
6180
|
-
progressBar.completeStep();
|
|
6181
|
-
try {
|
|
6182
|
-
import_fs_extra4.default.unlinkSync(compressedPath);
|
|
6183
|
-
} catch (error) {
|
|
6184
|
-
}
|
|
6185
|
-
const uploadData = {
|
|
6186
|
-
path: directoryPath,
|
|
6187
|
-
filename: import_path5.default.basename(directoryPath),
|
|
6188
|
-
contentHash: (result == null ? void 0 : result.hash) || "unknown",
|
|
6189
|
-
size: sizeCheck.size,
|
|
6190
|
-
fileCount: 0,
|
|
6191
|
-
isDirectory: true,
|
|
6192
|
-
shortUrl: (result == null ? void 0 : result.shortUrl) || null
|
|
6193
|
-
};
|
|
6194
|
-
saveUploadHistory(uploadData);
|
|
6195
|
-
if (!(result == null ? void 0 : result.hash)) {
|
|
6196
|
-
throw new Error("Server did not return valid hash value");
|
|
6197
|
-
}
|
|
6198
|
-
progressBar.complete();
|
|
6199
|
-
return result;
|
|
6200
|
-
} catch (error) {
|
|
6201
|
-
progressBar.fail(error.message);
|
|
6202
|
-
throw error;
|
|
6203
|
-
}
|
|
6204
|
-
}
|
|
6205
|
-
async function uploadFileInChunks(filePath, deviceId, importAsCar = false) {
|
|
6206
|
-
const sizeCheck = checkFileSizeLimit(filePath);
|
|
6207
|
-
if (sizeCheck.exceeds) {
|
|
6208
|
-
throw new Error(
|
|
6209
|
-
`File ${filePath} exceeds size limit ${formatSize(
|
|
6210
|
-
sizeCheck.limit
|
|
6211
|
-
)} (size: ${formatSize(sizeCheck.size)})`
|
|
6212
|
-
);
|
|
6213
|
-
}
|
|
6214
|
-
const fileName = import_path5.default.basename(filePath);
|
|
6215
|
-
const progressBar = new StepProgressBar(fileName, false);
|
|
6216
|
-
try {
|
|
6217
|
-
progressBar.startStep(0, "Initializing session");
|
|
6218
|
-
const sessionInfo = await initChunkSession(filePath, deviceId, false);
|
|
6219
|
-
progressBar.completeStep();
|
|
6220
|
-
progressBar.startStep(1, "Chunk upload");
|
|
6221
|
-
await uploadFileChunks(
|
|
6222
|
-
filePath,
|
|
6223
|
-
sessionInfo.session_id,
|
|
6224
|
-
sessionInfo.total_chunks,
|
|
6225
|
-
sessionInfo.chunk_size,
|
|
6226
|
-
deviceId,
|
|
6227
|
-
progressBar
|
|
6228
|
-
);
|
|
6229
|
-
progressBar.completeStep();
|
|
6230
|
-
progressBar.startStep(2, "Completing upload");
|
|
6231
|
-
const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
|
|
6232
|
-
progressBar.completeStep();
|
|
6233
|
-
progressBar.startStep(3, "Waiting for processing");
|
|
6234
|
-
const result = await monitorChunkProgress(traceId, deviceId, progressBar);
|
|
6235
|
-
progressBar.completeStep();
|
|
6236
|
-
const uploadData = {
|
|
6237
|
-
path: filePath,
|
|
6238
|
-
filename: fileName,
|
|
6239
|
-
contentHash: (result == null ? void 0 : result.hash) || "unknown",
|
|
6240
|
-
previewHash: null,
|
|
6241
|
-
size: sizeCheck.size,
|
|
6242
|
-
fileCount: 1,
|
|
6243
|
-
isDirectory: false,
|
|
6244
|
-
shortUrl: (result == null ? void 0 : result.shortUrl) || null
|
|
6245
|
-
};
|
|
6246
|
-
saveUploadHistory(uploadData);
|
|
6247
|
-
if (!(result == null ? void 0 : result.hash)) {
|
|
6248
|
-
throw new Error("Server did not return valid hash value");
|
|
6249
|
-
}
|
|
6250
|
-
progressBar.complete();
|
|
6251
|
-
return result;
|
|
6252
|
-
} catch (error) {
|
|
6253
|
-
progressBar.fail(error.message);
|
|
6254
|
-
throw error;
|
|
6255
|
-
}
|
|
6256
|
-
}
|
|
6257
|
-
async function uploadToIpfsSplit_default(filePath, importAsCar = false) {
|
|
6258
|
-
const deviceId = getUid();
|
|
6259
|
-
if (!deviceId) {
|
|
6260
|
-
throw new Error("Device ID not found");
|
|
6261
|
-
}
|
|
6262
|
-
try {
|
|
6263
|
-
const isDirectory = import_fs_extra4.default.statSync(filePath).isDirectory();
|
|
6264
|
-
const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId, importAsCar) : await uploadFileInChunks(filePath, deviceId, importAsCar);
|
|
6265
|
-
if (result == null ? void 0 : result.hash) {
|
|
6266
|
-
return {
|
|
6267
|
-
contentHash: result.hash,
|
|
6268
|
-
previewHash: null,
|
|
6269
|
-
shortUrl: result.shortUrl
|
|
6270
|
-
};
|
|
6271
|
-
}
|
|
6272
|
-
return null;
|
|
6273
|
-
} catch (error) {
|
|
6274
|
-
return null;
|
|
6275
|
-
}
|
|
6276
|
-
}
|
|
6277
|
-
|
|
6278
|
-
// bin/upload.ts
|
|
6279
|
-
var import_fs2 = __toESM(require("fs"));
|
|
6280
|
-
var import_crypto_js = __toESM(require("crypto-js"));
|
|
6281
|
-
|
|
6282
|
-
// bin/utils/pinmeApi.ts
|
|
6283
|
-
var import_chalk4 = __toESM(require("chalk"));
|
|
6284
|
-
|
|
6285
|
-
// bin/utils/webLogin.ts
|
|
6286
|
-
var import_crypto2 = __toESM(require("crypto"));
|
|
6287
|
-
var import_http4 = __toESM(require("http"));
|
|
6288
|
-
var import_url2 = require("url");
|
|
6289
|
-
var import_chalk3 = __toESM(require("chalk"));
|
|
6290
|
-
var import_child_process = require("child_process");
|
|
6291
|
-
var import_fs_extra5 = __toESM(require("fs-extra"));
|
|
6292
|
-
var import_os4 = __toESM(require("os"));
|
|
6293
|
-
var import_path6 = __toESM(require("path"));
|
|
6294
|
-
function openBrowser(url2) {
|
|
6295
|
-
const platform = process.platform;
|
|
6296
|
-
let command;
|
|
6297
|
-
if (platform === "darwin") {
|
|
6298
|
-
command = `open "${url2}"`;
|
|
6299
|
-
} else if (platform === "win32") {
|
|
6300
|
-
command = `start "" "${url2}"`;
|
|
6301
|
-
} else {
|
|
6302
|
-
command = `xdg-open "${url2}"`;
|
|
6303
|
-
}
|
|
6304
|
-
(0, import_child_process.exec)(command, (err) => {
|
|
6305
|
-
if (err) {
|
|
6306
|
-
console.log(import_chalk3.default.yellow(`Unable to open browser automatically. Please visit manually: ${url2}`));
|
|
6307
|
-
}
|
|
6308
|
-
});
|
|
6309
|
-
}
|
|
6310
|
-
var CONFIG_DIR2 = import_path6.default.join(import_os4.default.homedir(), ".pinme");
|
|
6311
|
-
var AUTH_FILE2 = import_path6.default.join(CONFIG_DIR2, "auth.json");
|
|
6312
|
-
var DEFAULT_OPTIONS = {
|
|
6313
|
-
apiBaseUrl: "https://pinme.benny1996.win/api/v4",
|
|
6314
|
-
webBaseUrl: process.env.PINME_WEB_URL || "http://localhost:5173",
|
|
6315
|
-
callbackPort: 34567,
|
|
6316
|
-
callbackPath: "/cli/callback"
|
|
6317
|
-
};
|
|
6318
|
-
var WebLoginManager = class {
|
|
6319
|
-
config;
|
|
6320
|
-
server = null;
|
|
6321
|
-
resolvePromise = null;
|
|
6322
|
-
rejectPromise = null;
|
|
6323
|
-
loginToken = "";
|
|
6324
|
-
constructor(options = {}) {
|
|
6325
|
-
this.config = { ...DEFAULT_OPTIONS, ...options };
|
|
6326
|
-
}
|
|
6327
|
-
async login() {
|
|
6328
|
-
console.log(import_chalk3.default.blue("Starting login flow...\n"));
|
|
6329
|
-
this.loginToken = this.generateLoginToken();
|
|
6330
|
-
console.log(import_chalk3.default.blue("Starting local callback server..."));
|
|
6331
|
-
await this.startCallbackServer();
|
|
6332
|
-
try {
|
|
6333
|
-
const loginUrl = this.buildLoginUrl();
|
|
6334
|
-
console.log(import_chalk3.default.blue("Opening browser..."));
|
|
6335
|
-
console.log(import_chalk3.default.white("If browser does not open automatically, please visit manually:"));
|
|
6336
|
-
console.log(import_chalk3.default.cyan(` ${loginUrl}
|
|
6337
|
-
`));
|
|
6338
|
-
openBrowser(loginUrl);
|
|
6339
|
-
console.log(import_chalk3.default.yellow("Please complete login in browser..."));
|
|
6340
|
-
console.log(import_chalk3.default.gray("Browser will close automatically after successful login.\n"));
|
|
6341
|
-
const authToken = await this.waitForCallback();
|
|
6342
|
-
const authConfig = this.parseAuthToken(authToken);
|
|
6343
|
-
this.saveAuthConfig(authConfig);
|
|
6344
|
-
console.log(import_chalk3.default.green("\nLogin successful!"));
|
|
6345
|
-
if (authConfig.email) {
|
|
6346
|
-
console.log(import_chalk3.default.green(`Welcome, ${authConfig.email}`));
|
|
6347
|
-
}
|
|
6348
|
-
console.log(import_chalk3.default.gray(`Address: ${authConfig.address}`));
|
|
6349
|
-
return authConfig;
|
|
6350
|
-
} catch (error) {
|
|
6351
|
-
console.error(import_chalk3.default.red(`
|
|
6352
|
-
Login failed: ${error.message}`));
|
|
6353
|
-
throw error;
|
|
6354
|
-
} finally {
|
|
6355
|
-
this.closeServer();
|
|
6356
|
-
}
|
|
6357
|
-
}
|
|
6358
|
-
generateLoginToken() {
|
|
6359
|
-
return import_crypto2.default.randomBytes(32).toString("hex");
|
|
6360
|
-
}
|
|
6361
|
-
async startCallbackServer() {
|
|
6362
|
-
return new Promise((resolve, reject) => {
|
|
6363
|
-
this.server = import_http4.default.createServer(async (req, res) => {
|
|
6364
|
-
try {
|
|
6365
|
-
const url2 = new import_url2.URL(req.url || "", `http://localhost:${this.config.callbackPort}`);
|
|
6366
|
-
if (url2.pathname === this.config.callbackPath) {
|
|
6367
|
-
const authToken = url2.searchParams.get("token");
|
|
6368
|
-
const error = url2.searchParams.get("error");
|
|
6369
|
-
const loginToken = url2.searchParams.get("login_token");
|
|
6370
|
-
if (error) {
|
|
6371
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
6372
|
-
res.end(this.getErrorHtml(error));
|
|
6373
|
-
if (this.rejectPromise) {
|
|
6374
|
-
this.rejectPromise(new Error(error));
|
|
6375
|
-
}
|
|
6376
|
-
return;
|
|
6377
|
-
}
|
|
6378
|
-
if (!authToken) {
|
|
6379
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
6380
|
-
res.end(this.getErrorHtml("Auth token not received"));
|
|
6381
|
-
if (this.rejectPromise) {
|
|
6382
|
-
this.rejectPromise(new Error("Auth token not received"));
|
|
6383
|
-
}
|
|
6384
|
-
return;
|
|
6385
|
-
}
|
|
6386
|
-
if (loginToken !== this.loginToken) {
|
|
6387
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
6388
|
-
res.end(this.getErrorHtml("Login token invalid or expired"));
|
|
6389
|
-
if (this.rejectPromise) {
|
|
6390
|
-
this.rejectPromise(new Error("Login token invalid or expired"));
|
|
6391
|
-
}
|
|
6392
|
-
return;
|
|
6393
|
-
}
|
|
6394
|
-
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
6395
|
-
res.end(this.getSuccessHtml());
|
|
6396
|
-
if (this.resolvePromise) {
|
|
6397
|
-
this.resolvePromise(authToken);
|
|
6398
|
-
}
|
|
6399
|
-
} else {
|
|
6400
|
-
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
6401
|
-
res.end("Not Found");
|
|
6402
|
-
}
|
|
6403
|
-
} catch (err) {
|
|
6404
|
-
console.error("Callback error:", err);
|
|
6405
|
-
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
6406
|
-
res.end("Internal Server Error");
|
|
6407
|
-
}
|
|
6408
|
-
});
|
|
6409
|
-
this.server.on("error", (err) => {
|
|
6410
|
-
reject(err);
|
|
6411
|
-
});
|
|
6412
|
-
this.server.listen(this.config.callbackPort, "127.0.0.1", () => {
|
|
6413
|
-
console.log(import_chalk3.default.gray(`Local server: http://localhost:${this.config.callbackPort}`));
|
|
6414
|
-
resolve();
|
|
6415
|
-
});
|
|
6416
|
-
setTimeout(() => {
|
|
6417
|
-
if (this.rejectPromise) {
|
|
6418
|
-
this.rejectPromise(new Error("Login timeout, please try again"));
|
|
6419
|
-
}
|
|
6420
|
-
this.closeServer();
|
|
6421
|
-
}, 10 * 60 * 1e3);
|
|
6422
|
-
});
|
|
6423
|
-
}
|
|
6424
|
-
buildLoginUrl() {
|
|
6425
|
-
const callbackUrl = `http://localhost:${this.config.callbackPort}${this.config.callbackPath}`;
|
|
6426
|
-
const url2 = `${this.config.webBaseUrl}/#/cli-login?login_token=${this.loginToken}&callback_url=${encodeURIComponent(callbackUrl)}`;
|
|
6427
|
-
return url2;
|
|
6428
|
-
}
|
|
6429
|
-
waitForCallback() {
|
|
6430
|
-
return new Promise((resolve, reject) => {
|
|
6431
|
-
this.resolvePromise = resolve;
|
|
6432
|
-
this.rejectPromise = reject;
|
|
6433
|
-
});
|
|
6434
|
-
}
|
|
6435
|
-
parseAuthToken(authToken) {
|
|
6436
|
-
const firstDash = authToken.indexOf("-");
|
|
6437
|
-
if (firstDash <= 0 || firstDash === authToken.length - 1) {
|
|
6438
|
-
throw new Error("Invalid token format");
|
|
6439
|
-
}
|
|
6440
|
-
const address = authToken.slice(0, firstDash).trim();
|
|
6441
|
-
const token = authToken.slice(firstDash + 1).trim();
|
|
6442
|
-
if (!address || !token) {
|
|
6443
|
-
throw new Error("Token parsing failed: address or token is empty");
|
|
6444
|
-
}
|
|
6445
|
-
return { address, token };
|
|
6446
|
-
}
|
|
6447
|
-
saveAuthConfig(config) {
|
|
6448
|
-
import_fs_extra5.default.ensureDirSync(CONFIG_DIR2);
|
|
6449
|
-
import_fs_extra5.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
|
|
6450
|
-
import_fs_extra5.default.chmodSync(AUTH_FILE2, 384);
|
|
6451
|
-
}
|
|
6452
|
-
closeServer() {
|
|
6453
|
-
if (this.server) {
|
|
6454
|
-
this.server.close();
|
|
6455
|
-
this.server = null;
|
|
5916
|
+
closeServer() {
|
|
5917
|
+
if (this.server) {
|
|
5918
|
+
this.server.close();
|
|
5919
|
+
this.server = null;
|
|
6456
5920
|
}
|
|
6457
5921
|
}
|
|
6458
5922
|
// HTML templates
|
|
@@ -6496,8 +5960,153 @@ Login failed: ${error.message}`));
|
|
|
6496
5960
|
width: 200%;
|
|
6497
5961
|
height: 200%;
|
|
6498
5962
|
background-image:
|
|
6499
|
-
linear-gradient(rgba(0, 200, 255, 0.03) 1px, transparent 1px),
|
|
6500
|
-
linear-gradient(90deg, rgba(0, 200, 255, 0.03) 1px, transparent 1px);
|
|
5963
|
+
linear-gradient(rgba(0, 200, 255, 0.03) 1px, transparent 1px),
|
|
5964
|
+
linear-gradient(90deg, rgba(0, 200, 255, 0.03) 1px, transparent 1px);
|
|
5965
|
+
background-size: 50px 50px;
|
|
5966
|
+
transform: perspective(500px) rotateX(60deg) translateY(-50%) translateZ(-200px);
|
|
5967
|
+
animation: gridMove 20s linear infinite;
|
|
5968
|
+
}
|
|
5969
|
+
@keyframes gridMove {
|
|
5970
|
+
0% { transform: perspective(500px) rotateX(60deg) translateY(0) translateZ(-200px); }
|
|
5971
|
+
100% { transform: perspective(500px) rotateX(60deg) translateY(50px) translateZ(-200px); }
|
|
5972
|
+
}
|
|
5973
|
+
.container {
|
|
5974
|
+
position: relative;
|
|
5975
|
+
z-index: 10;
|
|
5976
|
+
background: linear-gradient(135deg, rgba(20, 20, 40, 0.9) 0%, rgba(10, 10, 30, 0.95) 100%);
|
|
5977
|
+
padding: 3.5rem 4rem;
|
|
5978
|
+
border-radius: 32px;
|
|
5979
|
+
box-shadow:
|
|
5980
|
+
0 0 60px rgba(0, 200, 255, 0.15),
|
|
5981
|
+
0 25px 50px rgba(0, 0, 0, 0.5),
|
|
5982
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
|
5983
|
+
inset 0 -1px 0 rgba(0, 200, 255, 0.1);
|
|
5984
|
+
text-align: center;
|
|
5985
|
+
border: 1px solid rgba(0, 200, 255, 0.2);
|
|
5986
|
+
max-width: 440px;
|
|
5987
|
+
backdrop-filter: blur(30px);
|
|
5988
|
+
}
|
|
5989
|
+
.container::before {
|
|
5990
|
+
content: '';
|
|
5991
|
+
position: absolute;
|
|
5992
|
+
top: -1px;
|
|
5993
|
+
left: -1px;
|
|
5994
|
+
right: -1px;
|
|
5995
|
+
bottom: -1px;
|
|
5996
|
+
border-radius: 32px;
|
|
5997
|
+
background: linear-gradient(135deg, rgba(0, 200, 255, 0.5), rgba(255, 0, 150, 0.5), rgba(120, 0, 255, 0.5));
|
|
5998
|
+
z-index: -1;
|
|
5999
|
+
opacity: 0.5;
|
|
6000
|
+
animation: borderGlow 3s ease-in-out infinite;
|
|
6001
|
+
}
|
|
6002
|
+
@keyframes borderGlow {
|
|
6003
|
+
0%, 100% { opacity: 0.3; }
|
|
6004
|
+
50% { opacity: 0.7; }
|
|
6005
|
+
}
|
|
6006
|
+
.success-icon {
|
|
6007
|
+
font-size: 5rem;
|
|
6008
|
+
margin-bottom: 1.5rem;
|
|
6009
|
+
animation: bounceIn 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
|
6010
|
+
filter: drop-shadow(0 0 20px rgba(0, 200, 255, 0.5));
|
|
6011
|
+
}
|
|
6012
|
+
@keyframes bounceIn {
|
|
6013
|
+
0% { transform: scale(0); opacity: 0; }
|
|
6014
|
+
50% { transform: scale(1.2); }
|
|
6015
|
+
100% { transform: scale(1); opacity: 1; }
|
|
6016
|
+
}
|
|
6017
|
+
h1 {
|
|
6018
|
+
color: #fff;
|
|
6019
|
+
font-size: 2.2rem;
|
|
6020
|
+
font-weight: 700;
|
|
6021
|
+
margin: 0 0 0.75rem 0;
|
|
6022
|
+
background: linear-gradient(90deg, #fff, #00d4ff);
|
|
6023
|
+
-webkit-background-clip: text;
|
|
6024
|
+
-webkit-text-fill-color: transparent;
|
|
6025
|
+
background-clip: text;
|
|
6026
|
+
}
|
|
6027
|
+
p {
|
|
6028
|
+
color: rgba(255, 255, 255, 0.6);
|
|
6029
|
+
font-size: 1.1rem;
|
|
6030
|
+
margin: 0 0 2rem 0;
|
|
6031
|
+
line-height: 1.6;
|
|
6032
|
+
}
|
|
6033
|
+
.highlight { color: #00d4ff; font-weight: 600; }
|
|
6034
|
+
.sparkle {
|
|
6035
|
+
position: absolute;
|
|
6036
|
+
width: 4px;
|
|
6037
|
+
height: 4px;
|
|
6038
|
+
background: #00d4ff;
|
|
6039
|
+
border-radius: 50%;
|
|
6040
|
+
animation: sparkle 2s ease-in-out infinite;
|
|
6041
|
+
}
|
|
6042
|
+
.sparkle:nth-child(1) { top: 20%; left: 10%; animation-delay: 0s; }
|
|
6043
|
+
.sparkle:nth-child(2) { top: 30%; right: 15%; animation-delay: 0.5s; }
|
|
6044
|
+
.sparkle:nth-child(3) { bottom: 25%; left: 20%; animation-delay: 1s; }
|
|
6045
|
+
.sparkle:nth-child(4) { bottom: 35%; right: 10%; animation-delay: 1.5s; }
|
|
6046
|
+
@keyframes sparkle {
|
|
6047
|
+
0%, 100% { opacity: 0; transform: scale(0); }
|
|
6048
|
+
50% { opacity: 1; transform: scale(1); }
|
|
6049
|
+
}
|
|
6050
|
+
</style>
|
|
6051
|
+
</head>
|
|
6052
|
+
<body>
|
|
6053
|
+
<div class="bg"></div>
|
|
6054
|
+
<div class="grid"></div>
|
|
6055
|
+
<div class="container">
|
|
6056
|
+
<div class="sparkle"></div>
|
|
6057
|
+
<div class="sparkle"></div>
|
|
6058
|
+
<div class="sparkle"></div>
|
|
6059
|
+
<div class="sparkle"></div>
|
|
6060
|
+
<div class="success-icon">\u{1F389}</div>
|
|
6061
|
+
<h1>Welcome to PinMe</h1>
|
|
6062
|
+
<p>You are now logged in! <span class="highlight">\u{1F680}</span><br>Return to your terminal to continue.</p>
|
|
6063
|
+
</div>
|
|
6064
|
+
</body>
|
|
6065
|
+
</html>`;
|
|
6066
|
+
}
|
|
6067
|
+
getErrorHtml(error) {
|
|
6068
|
+
const encodedError = encodeURIComponent(error);
|
|
6069
|
+
return `
|
|
6070
|
+
<!DOCTYPE html>
|
|
6071
|
+
<html>
|
|
6072
|
+
<head>
|
|
6073
|
+
<title>Login Failed - PinMe</title>
|
|
6074
|
+
<style>
|
|
6075
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6076
|
+
body {
|
|
6077
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
6078
|
+
display: flex;
|
|
6079
|
+
justify-content: center;
|
|
6080
|
+
align-items: center;
|
|
6081
|
+
min-height: 100vh;
|
|
6082
|
+
background: #000;
|
|
6083
|
+
overflow: hidden;
|
|
6084
|
+
}
|
|
6085
|
+
.bg {
|
|
6086
|
+
position: fixed;
|
|
6087
|
+
top: 0;
|
|
6088
|
+
left: 0;
|
|
6089
|
+
width: 100%;
|
|
6090
|
+
height: 100%;
|
|
6091
|
+
background:
|
|
6092
|
+
radial-gradient(ellipse at 20% 80%, rgba(255, 50, 50, 0.2) 0%, transparent 50%),
|
|
6093
|
+
radial-gradient(ellipse at 80% 20%, rgba(255, 100, 50, 0.2) 0%, transparent 50%),
|
|
6094
|
+
radial-gradient(ellipse at 50% 50%, rgba(100, 0, 50, 0.15) 0%, transparent 60%);
|
|
6095
|
+
animation: bgPulse 6s ease-in-out infinite;
|
|
6096
|
+
}
|
|
6097
|
+
@keyframes bgPulse {
|
|
6098
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
6099
|
+
50% { opacity: 0.8; transform: scale(1.05); }
|
|
6100
|
+
}
|
|
6101
|
+
.grid {
|
|
6102
|
+
position: fixed;
|
|
6103
|
+
top: 0;
|
|
6104
|
+
left: 0;
|
|
6105
|
+
width: 200%;
|
|
6106
|
+
height: 200%;
|
|
6107
|
+
background-image:
|
|
6108
|
+
linear-gradient(rgba(255, 80, 80, 0.03) 1px, transparent 1px),
|
|
6109
|
+
linear-gradient(90deg, rgba(255, 80, 80, 0.03) 1px, transparent 1px);
|
|
6501
6110
|
background-size: 50px 50px;
|
|
6502
6111
|
transform: perspective(500px) rotateX(60deg) translateY(-50%) translateZ(-200px);
|
|
6503
6112
|
animation: gridMove 20s linear infinite;
|
|
@@ -6509,16 +6118,16 @@ Login failed: ${error.message}`));
|
|
|
6509
6118
|
.container {
|
|
6510
6119
|
position: relative;
|
|
6511
6120
|
z-index: 10;
|
|
6512
|
-
background: linear-gradient(135deg, rgba(
|
|
6121
|
+
background: linear-gradient(135deg, rgba(40, 20, 20, 0.9) 0%, rgba(30, 10, 10, 0.95) 100%);
|
|
6513
6122
|
padding: 3.5rem 4rem;
|
|
6514
6123
|
border-radius: 32px;
|
|
6515
6124
|
box-shadow:
|
|
6516
|
-
0 0 60px rgba(
|
|
6125
|
+
0 0 60px rgba(255, 50, 50, 0.15),
|
|
6517
6126
|
0 25px 50px rgba(0, 0, 0, 0.5),
|
|
6518
6127
|
inset 0 1px 0 rgba(255, 255, 255, 0.1),
|
|
6519
|
-
inset 0 -1px 0 rgba(
|
|
6128
|
+
inset 0 -1px 0 rgba(255, 50, 50, 0.1);
|
|
6520
6129
|
text-align: center;
|
|
6521
|
-
border: 1px solid rgba(
|
|
6130
|
+
border: 1px solid rgba(255, 50, 50, 0.2);
|
|
6522
6131
|
max-width: 440px;
|
|
6523
6132
|
backdrop-filter: blur(30px);
|
|
6524
6133
|
}
|
|
@@ -6530,7 +6139,7 @@ Login failed: ${error.message}`));
|
|
|
6530
6139
|
right: -1px;
|
|
6531
6140
|
bottom: -1px;
|
|
6532
6141
|
border-radius: 32px;
|
|
6533
|
-
background: linear-gradient(135deg, rgba(
|
|
6142
|
+
background: linear-gradient(135deg, rgba(255, 50, 50, 0.5), rgba(255, 150, 50, 0.5), rgba(150, 0, 50, 0.5));
|
|
6534
6143
|
z-index: -1;
|
|
6535
6144
|
opacity: 0.5;
|
|
6536
6145
|
animation: borderGlow 3s ease-in-out infinite;
|
|
@@ -6539,246 +6148,640 @@ Login failed: ${error.message}`));
|
|
|
6539
6148
|
0%, 100% { opacity: 0.3; }
|
|
6540
6149
|
50% { opacity: 0.7; }
|
|
6541
6150
|
}
|
|
6542
|
-
.
|
|
6151
|
+
.error-icon {
|
|
6543
6152
|
font-size: 5rem;
|
|
6544
6153
|
margin-bottom: 1.5rem;
|
|
6545
|
-
animation:
|
|
6546
|
-
filter: drop-shadow(0 0 20px rgba(
|
|
6154
|
+
animation: shake 0.5s ease-in-out;
|
|
6155
|
+
filter: drop-shadow(0 0 20px rgba(255, 50, 50, 0.5));
|
|
6547
6156
|
}
|
|
6548
|
-
@keyframes
|
|
6549
|
-
0% { transform:
|
|
6550
|
-
|
|
6551
|
-
|
|
6157
|
+
@keyframes shake {
|
|
6158
|
+
0%, 100% { transform: translateX(0); }
|
|
6159
|
+
20% { transform: translateX(-10px) rotate(-5deg); }
|
|
6160
|
+
40% { transform: translateX(10px) rotate(5deg); }
|
|
6161
|
+
60% { transform: translateX(-10px) rotate(-5deg); }
|
|
6162
|
+
80% { transform: translateX(10px) rotate(5deg); }
|
|
6552
6163
|
}
|
|
6553
6164
|
h1 {
|
|
6554
6165
|
color: #fff;
|
|
6555
6166
|
font-size: 2.2rem;
|
|
6556
6167
|
font-weight: 700;
|
|
6557
6168
|
margin: 0 0 0.75rem 0;
|
|
6558
|
-
background: linear-gradient(90deg, #fff, #
|
|
6169
|
+
background: linear-gradient(90deg, #fff, #ff5050);
|
|
6559
6170
|
-webkit-background-clip: text;
|
|
6560
6171
|
-webkit-text-fill-color: transparent;
|
|
6561
6172
|
background-clip: text;
|
|
6562
6173
|
}
|
|
6563
|
-
|
|
6564
|
-
color:
|
|
6565
|
-
font-size:
|
|
6174
|
+
.error {
|
|
6175
|
+
color: #ff6b6b;
|
|
6176
|
+
font-size: 1rem;
|
|
6566
6177
|
margin: 0 0 2rem 0;
|
|
6567
|
-
|
|
6178
|
+
padding: 1.25rem;
|
|
6179
|
+
background: rgba(255, 50, 50, 0.1);
|
|
6180
|
+
border-radius: 16px;
|
|
6181
|
+
border: 1px solid rgba(255, 50, 50, 0.2);
|
|
6182
|
+
font-weight: 500;
|
|
6183
|
+
box-shadow: 0 0 20px rgba(255, 50, 50, 0.1);
|
|
6568
6184
|
}
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6185
|
+
</style>
|
|
6186
|
+
</head>
|
|
6187
|
+
<body>
|
|
6188
|
+
<div class="bg"></div>
|
|
6189
|
+
<div class="grid"></div>
|
|
6190
|
+
<div class="container">
|
|
6191
|
+
<div class="error-icon">\u{1F635}</div>
|
|
6192
|
+
<h1>Oops!</h1>
|
|
6193
|
+
<div class="error">${error}</div>
|
|
6194
|
+
</div>
|
|
6195
|
+
</body>
|
|
6196
|
+
</html>`;
|
|
6197
|
+
}
|
|
6198
|
+
};
|
|
6199
|
+
var webLoginManager = new WebLoginManager();
|
|
6200
|
+
function setAuthToken(combined) {
|
|
6201
|
+
const firstDash = combined.indexOf("-");
|
|
6202
|
+
if (firstDash <= 0 || firstDash === combined.length - 1) {
|
|
6203
|
+
throw new Error('Invalid token format. Expected "<address>-<jwt>".');
|
|
6204
|
+
}
|
|
6205
|
+
const address = combined.slice(0, firstDash).trim();
|
|
6206
|
+
const token = combined.slice(firstDash + 1).trim();
|
|
6207
|
+
if (!address || !token) {
|
|
6208
|
+
throw new Error("Invalid token content. Address or token is empty.");
|
|
6209
|
+
}
|
|
6210
|
+
const config = { address, token };
|
|
6211
|
+
import_fs_extra4.default.ensureDirSync(CONFIG_DIR2);
|
|
6212
|
+
import_fs_extra4.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
|
|
6213
|
+
return config;
|
|
6214
|
+
}
|
|
6215
|
+
function getAuthConfig2() {
|
|
6216
|
+
try {
|
|
6217
|
+
if (!import_fs_extra4.default.existsSync(AUTH_FILE2)) return null;
|
|
6218
|
+
const data = import_fs_extra4.default.readJsonSync(AUTH_FILE2);
|
|
6219
|
+
if (!(data == null ? void 0 : data.address) || !(data == null ? void 0 : data.token)) return null;
|
|
6220
|
+
return data;
|
|
6221
|
+
} catch {
|
|
6222
|
+
return null;
|
|
6223
|
+
}
|
|
6224
|
+
}
|
|
6225
|
+
function clearAuthToken() {
|
|
6226
|
+
try {
|
|
6227
|
+
if (import_fs_extra4.default.existsSync(AUTH_FILE2)) {
|
|
6228
|
+
import_fs_extra4.default.removeSync(AUTH_FILE2);
|
|
6229
|
+
}
|
|
6230
|
+
} catch (error) {
|
|
6231
|
+
console.error(`Failed to clear auth token: ${error}`);
|
|
6232
|
+
}
|
|
6233
|
+
}
|
|
6234
|
+
function getAuthHeaders() {
|
|
6235
|
+
const conf = getAuthConfig2();
|
|
6236
|
+
if (!conf) {
|
|
6237
|
+
throw new Error("Auth not set. Run: pinme login");
|
|
6238
|
+
}
|
|
6239
|
+
return {
|
|
6240
|
+
"token-address": conf.address,
|
|
6241
|
+
"authentication-tokens": conf.token
|
|
6242
|
+
};
|
|
6243
|
+
}
|
|
6244
|
+
|
|
6245
|
+
// bin/utils/uploadToIpfsSplit.ts
|
|
6246
|
+
var IPFS_API_URL = "https://pinme.benny1996.win/api/v3";
|
|
6247
|
+
var MAX_RETRIES = parseInt(process.env.MAX_RETRIES || "2");
|
|
6248
|
+
var RETRY_DELAY = parseInt(process.env.RETRY_DELAY_MS || "1000");
|
|
6249
|
+
var TIMEOUT = parseInt(process.env.TIMEOUT_MS || "600000");
|
|
6250
|
+
var MAX_POLL_TIME = parseInt("5") * 60 * 1e3;
|
|
6251
|
+
var POLL_INTERVAL = parseInt(process.env.POLL_INTERVAL_SECONDS || "2") * 1e3;
|
|
6252
|
+
var PROGRESS_UPDATE_INTERVAL = 200;
|
|
6253
|
+
var EXPECTED_UPLOAD_TIME = 6e4;
|
|
6254
|
+
var MAX_PROGRESS = 0.9;
|
|
6255
|
+
var StepProgressBar = class {
|
|
6256
|
+
spinner;
|
|
6257
|
+
fileName;
|
|
6258
|
+
startTime;
|
|
6259
|
+
currentStep = 0;
|
|
6260
|
+
stepStartTime = 0;
|
|
6261
|
+
progressInterval = null;
|
|
6262
|
+
isSimulatingProgress = false;
|
|
6263
|
+
simulationStartTime = 0;
|
|
6264
|
+
constructor(fileName, isDirectory = false) {
|
|
6265
|
+
this.fileName = fileName;
|
|
6266
|
+
this.spinner = (0, import_ora.default)(`Preparing to upload ${fileName}...`).start();
|
|
6267
|
+
this.startTime = Date.now();
|
|
6268
|
+
this.stepStartTime = Date.now();
|
|
6269
|
+
this.startProgress();
|
|
6270
|
+
}
|
|
6271
|
+
startStep(stepIndex, stepName) {
|
|
6272
|
+
this.currentStep = stepIndex;
|
|
6273
|
+
this.stepStartTime = Date.now();
|
|
6274
|
+
}
|
|
6275
|
+
updateProgress(progress, total) {
|
|
6276
|
+
}
|
|
6277
|
+
completeStep() {
|
|
6278
|
+
}
|
|
6279
|
+
// Start simulating progress to continue display after 90%
|
|
6280
|
+
startSimulatingProgress() {
|
|
6281
|
+
this.isSimulatingProgress = true;
|
|
6282
|
+
this.simulationStartTime = Date.now();
|
|
6283
|
+
}
|
|
6284
|
+
// Stop simulating progress
|
|
6285
|
+
stopSimulatingProgress() {
|
|
6286
|
+
this.isSimulatingProgress = false;
|
|
6287
|
+
}
|
|
6288
|
+
failStep(error) {
|
|
6289
|
+
this.stopProgress();
|
|
6290
|
+
this.spinner.fail(`Upload failed: ${error}`);
|
|
6291
|
+
}
|
|
6292
|
+
complete() {
|
|
6293
|
+
this.stopProgress();
|
|
6294
|
+
const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
|
|
6295
|
+
const progressBar = this.createProgressBar(1);
|
|
6296
|
+
this.spinner.succeed(
|
|
6297
|
+
`Upload completed ${progressBar} 100% (${totalTime}s)`
|
|
6298
|
+
);
|
|
6299
|
+
}
|
|
6300
|
+
fail(error) {
|
|
6301
|
+
this.stopProgress();
|
|
6302
|
+
const totalTime = Math.floor((Date.now() - this.startTime) / 1e3);
|
|
6303
|
+
this.spinner.fail(`Upload failed: ${error} (${totalTime}s)`);
|
|
6304
|
+
}
|
|
6305
|
+
startProgress() {
|
|
6306
|
+
this.progressInterval = setInterval(() => {
|
|
6307
|
+
const elapsed = Date.now() - this.startTime;
|
|
6308
|
+
let progress;
|
|
6309
|
+
if (this.isSimulatingProgress) {
|
|
6310
|
+
const simulationElapsed = Date.now() - this.simulationStartTime;
|
|
6311
|
+
const simulationProgress = Math.min(simulationElapsed / 6e4, 1);
|
|
6312
|
+
progress = 0.9 + simulationProgress * 0.09;
|
|
6313
|
+
} else {
|
|
6314
|
+
progress = this.calculateProgress(elapsed);
|
|
6315
|
+
}
|
|
6316
|
+
const duration = this.formatDuration(Math.floor(elapsed / 1e3));
|
|
6317
|
+
const progressBar = this.createProgressBar(progress);
|
|
6318
|
+
this.spinner.text = `Uploading ${this.fileName}... ${progressBar} ${Math.round(progress * 100)}% (${duration})`;
|
|
6319
|
+
}, PROGRESS_UPDATE_INTERVAL);
|
|
6320
|
+
}
|
|
6321
|
+
stopProgress() {
|
|
6322
|
+
if (this.progressInterval) {
|
|
6323
|
+
clearInterval(this.progressInterval);
|
|
6324
|
+
this.progressInterval = null;
|
|
6325
|
+
}
|
|
6326
|
+
}
|
|
6327
|
+
calculateProgress(elapsed) {
|
|
6328
|
+
return Math.min(
|
|
6329
|
+
elapsed / EXPECTED_UPLOAD_TIME * MAX_PROGRESS,
|
|
6330
|
+
MAX_PROGRESS
|
|
6331
|
+
);
|
|
6332
|
+
}
|
|
6333
|
+
createProgressBar(progress, width = 20) {
|
|
6334
|
+
const percentage = Math.min(progress, 1);
|
|
6335
|
+
const filledWidth = Math.round(width * percentage);
|
|
6336
|
+
const emptyWidth = width - filledWidth;
|
|
6337
|
+
return `[${"\u2588".repeat(filledWidth)}${"\u2591".repeat(emptyWidth)}]`;
|
|
6338
|
+
}
|
|
6339
|
+
formatDuration(seconds) {
|
|
6340
|
+
if (seconds < 60) {
|
|
6341
|
+
return `${seconds}s`;
|
|
6342
|
+
} else if (seconds < 3600) {
|
|
6343
|
+
const minutes = Math.floor(seconds / 60);
|
|
6344
|
+
const remainingSeconds = seconds % 60;
|
|
6345
|
+
return `${minutes}m ${remainingSeconds}s`;
|
|
6346
|
+
} else {
|
|
6347
|
+
const hours = Math.floor(seconds / 3600);
|
|
6348
|
+
const minutes = Math.floor(seconds % 3600 / 60);
|
|
6349
|
+
const remainingSeconds = seconds % 60;
|
|
6350
|
+
return `${hours}h ${minutes}m ${remainingSeconds}s`;
|
|
6351
|
+
}
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6354
|
+
async function calculateMD5(filePath) {
|
|
6355
|
+
return new Promise((resolve, reject) => {
|
|
6356
|
+
const hash = crypto3.createHash("md5");
|
|
6357
|
+
const stream4 = import_fs_extra5.default.createReadStream(filePath);
|
|
6358
|
+
stream4.on("data", hash.update.bind(hash));
|
|
6359
|
+
stream4.on("end", () => resolve(hash.digest("hex")));
|
|
6360
|
+
stream4.on("error", reject);
|
|
6361
|
+
});
|
|
6362
|
+
}
|
|
6363
|
+
async function compressDirectory(sourcePath) {
|
|
6364
|
+
return new Promise((resolve, reject) => {
|
|
6365
|
+
const tempDir = require("os").tmpdir();
|
|
6366
|
+
if (!import_fs_extra5.default.existsSync(tempDir)) {
|
|
6367
|
+
import_fs_extra5.default.mkdirSync(tempDir, { recursive: true });
|
|
6368
|
+
}
|
|
6369
|
+
const outputPath = import_path6.default.join(
|
|
6370
|
+
tempDir,
|
|
6371
|
+
`pinme_${import_path6.default.basename(sourcePath)}_${Date.now()}.zip`
|
|
6372
|
+
);
|
|
6373
|
+
const output = import_fs_extra5.default.createWriteStream(outputPath);
|
|
6374
|
+
const zlib2 = require("zlib");
|
|
6375
|
+
const gzip = zlib2.createGzip({ level: 9 });
|
|
6376
|
+
output.on("close", () => resolve(outputPath));
|
|
6377
|
+
gzip.on("error", reject);
|
|
6378
|
+
gzip.pipe(output);
|
|
6379
|
+
const stats = import_fs_extra5.default.statSync(sourcePath);
|
|
6380
|
+
if (stats.isDirectory()) {
|
|
6381
|
+
const archive = require("archiver");
|
|
6382
|
+
const archiveStream = archive("zip", { zlib: { level: 9 } });
|
|
6383
|
+
archiveStream.on("error", reject);
|
|
6384
|
+
archiveStream.pipe(output);
|
|
6385
|
+
archiveStream.directory(sourcePath, false);
|
|
6386
|
+
archiveStream.finalize();
|
|
6387
|
+
} else {
|
|
6388
|
+
const fileStream = import_fs_extra5.default.createReadStream(sourcePath);
|
|
6389
|
+
fileStream.pipe(gzip);
|
|
6390
|
+
}
|
|
6391
|
+
});
|
|
6392
|
+
}
|
|
6393
|
+
async function initChunkSession(filePath, deviceId, isDirectory = false) {
|
|
6394
|
+
const stats = import_fs_extra5.default.statSync(filePath);
|
|
6395
|
+
const fileName = import_path6.default.basename(filePath);
|
|
6396
|
+
const fileSize = stats.size;
|
|
6397
|
+
const md5 = await calculateMD5(filePath);
|
|
6398
|
+
try {
|
|
6399
|
+
const response = await axios_default.post(
|
|
6400
|
+
`${IPFS_API_URL}/chunk/init`,
|
|
6401
|
+
{
|
|
6402
|
+
file_name: fileName,
|
|
6403
|
+
file_size: fileSize,
|
|
6404
|
+
md5,
|
|
6405
|
+
is_directory: isDirectory,
|
|
6406
|
+
uid: deviceId
|
|
6407
|
+
},
|
|
6408
|
+
{
|
|
6409
|
+
timeout: TIMEOUT,
|
|
6410
|
+
headers: { "Content-Type": "application/json" }
|
|
6411
|
+
}
|
|
6412
|
+
);
|
|
6413
|
+
const { code, msg, data } = response.data;
|
|
6414
|
+
if (code === 200 && data) {
|
|
6415
|
+
return data;
|
|
6577
6416
|
}
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
.
|
|
6581
|
-
|
|
6582
|
-
@keyframes sparkle {
|
|
6583
|
-
0%, 100% { opacity: 0; transform: scale(0); }
|
|
6584
|
-
50% { opacity: 1; transform: scale(1); }
|
|
6417
|
+
throw new Error(`Session initialization failed: ${msg} (code: ${code})`);
|
|
6418
|
+
} catch (error) {
|
|
6419
|
+
if (axios_default.isAxiosError(error)) {
|
|
6420
|
+
throw new Error(`Network error: ${error.message}`);
|
|
6585
6421
|
}
|
|
6586
|
-
|
|
6587
|
-
</head>
|
|
6588
|
-
<body>
|
|
6589
|
-
<div class="bg"></div>
|
|
6590
|
-
<div class="grid"></div>
|
|
6591
|
-
<div class="container">
|
|
6592
|
-
<div class="sparkle"></div>
|
|
6593
|
-
<div class="sparkle"></div>
|
|
6594
|
-
<div class="sparkle"></div>
|
|
6595
|
-
<div class="sparkle"></div>
|
|
6596
|
-
<div class="success-icon">\u{1F389}</div>
|
|
6597
|
-
<h1>Welcome to PinMe</h1>
|
|
6598
|
-
<p>You are now logged in! <span class="highlight">\u{1F680}</span><br>Return to your terminal to continue.</p>
|
|
6599
|
-
</div>
|
|
6600
|
-
</body>
|
|
6601
|
-
</html>`;
|
|
6422
|
+
throw error;
|
|
6602
6423
|
}
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
<head>
|
|
6609
|
-
<title>Login Failed - PinMe</title>
|
|
6610
|
-
<style>
|
|
6611
|
-
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
6612
|
-
body {
|
|
6613
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
6614
|
-
display: flex;
|
|
6615
|
-
justify-content: center;
|
|
6616
|
-
align-items: center;
|
|
6617
|
-
min-height: 100vh;
|
|
6618
|
-
background: #000;
|
|
6619
|
-
overflow: hidden;
|
|
6424
|
+
}
|
|
6425
|
+
async function uploadChunkWithAbort(sessionId, chunkIndex, chunkData, deviceId, signal, retryCount = 0) {
|
|
6426
|
+
try {
|
|
6427
|
+
if (signal.aborted) {
|
|
6428
|
+
throw new Error("Request cancelled");
|
|
6620
6429
|
}
|
|
6621
|
-
.
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6430
|
+
const form = new import_form_data2.default();
|
|
6431
|
+
form.append("session_id", sessionId);
|
|
6432
|
+
form.append("chunk_index", chunkIndex.toString());
|
|
6433
|
+
form.append("uid", deviceId);
|
|
6434
|
+
form.append("chunk", chunkData, {
|
|
6435
|
+
filename: `chunk_${chunkIndex}`,
|
|
6436
|
+
contentType: "application/octet-stream"
|
|
6437
|
+
});
|
|
6438
|
+
const response = await axios_default.post(
|
|
6439
|
+
`${IPFS_API_URL}/chunk/upload`,
|
|
6440
|
+
form,
|
|
6441
|
+
{
|
|
6442
|
+
headers: { ...form.getHeaders() },
|
|
6443
|
+
timeout: TIMEOUT,
|
|
6444
|
+
signal
|
|
6445
|
+
}
|
|
6446
|
+
);
|
|
6447
|
+
const { code, msg, data } = response.data;
|
|
6448
|
+
if (code === 200 && data) {
|
|
6449
|
+
return data;
|
|
6632
6450
|
}
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6451
|
+
throw new Error(`Chunk upload failed: ${msg} (code: ${code})`);
|
|
6452
|
+
} catch (error) {
|
|
6453
|
+
if (error.name === "CanceledError" || signal.aborted) {
|
|
6454
|
+
throw new Error("Request cancelled");
|
|
6636
6455
|
}
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
transform: perspective(500px) rotateX(60deg) translateY(-50%) translateZ(-200px);
|
|
6648
|
-
animation: gridMove 20s linear infinite;
|
|
6456
|
+
if (retryCount < MAX_RETRIES) {
|
|
6457
|
+
await delayWithAbortCheck(RETRY_DELAY, signal);
|
|
6458
|
+
return uploadChunkWithAbort(
|
|
6459
|
+
sessionId,
|
|
6460
|
+
chunkIndex,
|
|
6461
|
+
chunkData,
|
|
6462
|
+
deviceId,
|
|
6463
|
+
signal,
|
|
6464
|
+
retryCount + 1
|
|
6465
|
+
);
|
|
6649
6466
|
}
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6467
|
+
throw new Error(
|
|
6468
|
+
`Chunk ${chunkIndex + 1} upload failed after ${MAX_RETRIES} retries: ${error.message}`
|
|
6469
|
+
);
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
async function delayWithAbortCheck(delay, signal) {
|
|
6473
|
+
return new Promise((resolve, reject) => {
|
|
6474
|
+
const timeoutId = setTimeout(() => {
|
|
6475
|
+
if (signal.aborted) {
|
|
6476
|
+
reject(new Error("Request cancelled"));
|
|
6477
|
+
} else {
|
|
6478
|
+
resolve();
|
|
6479
|
+
}
|
|
6480
|
+
}, delay);
|
|
6481
|
+
if (signal.aborted) {
|
|
6482
|
+
clearTimeout(timeoutId);
|
|
6483
|
+
reject(new Error("Request cancelled"));
|
|
6484
|
+
return;
|
|
6653
6485
|
}
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6486
|
+
const checkInterval = setInterval(() => {
|
|
6487
|
+
if (signal.aborted) {
|
|
6488
|
+
clearTimeout(timeoutId);
|
|
6489
|
+
clearInterval(checkInterval);
|
|
6490
|
+
reject(new Error("Request cancelled"));
|
|
6491
|
+
}
|
|
6492
|
+
}, 50);
|
|
6493
|
+
});
|
|
6494
|
+
}
|
|
6495
|
+
async function uploadFileChunks(filePath, sessionId, totalChunks, chunkSize, deviceId, progressBar) {
|
|
6496
|
+
const fileData = import_fs_extra5.default.readFileSync(filePath);
|
|
6497
|
+
const abortController = new AbortController();
|
|
6498
|
+
let completedCount = 0;
|
|
6499
|
+
let hasFatalError = false;
|
|
6500
|
+
let fatalError = null;
|
|
6501
|
+
const uploadTasks = Array.from({ length: totalChunks }, (_, chunkIndex) => {
|
|
6502
|
+
const start = chunkIndex * chunkSize;
|
|
6503
|
+
const end = Math.min(start + chunkSize, fileData.length);
|
|
6504
|
+
const chunkData = fileData.slice(start, end);
|
|
6505
|
+
return async () => {
|
|
6506
|
+
if (abortController.signal.aborted) return;
|
|
6507
|
+
try {
|
|
6508
|
+
await uploadChunkWithAbort(
|
|
6509
|
+
sessionId,
|
|
6510
|
+
chunkIndex,
|
|
6511
|
+
chunkData,
|
|
6512
|
+
deviceId,
|
|
6513
|
+
abortController.signal
|
|
6514
|
+
);
|
|
6515
|
+
if (abortController.signal.aborted) return;
|
|
6516
|
+
completedCount++;
|
|
6517
|
+
progressBar.updateProgress(completedCount, totalChunks);
|
|
6518
|
+
} catch (error) {
|
|
6519
|
+
if (error.name === "AbortError" || abortController.signal.aborted) {
|
|
6520
|
+
return;
|
|
6521
|
+
}
|
|
6522
|
+
hasFatalError = true;
|
|
6523
|
+
fatalError = `Chunk ${chunkIndex + 1}/${totalChunks} upload failed: ${error.message}`;
|
|
6524
|
+
abortController.abort();
|
|
6525
|
+
throw new Error(fatalError);
|
|
6526
|
+
}
|
|
6527
|
+
};
|
|
6528
|
+
});
|
|
6529
|
+
try {
|
|
6530
|
+
const results = await Promise.allSettled(uploadTasks.map((task) => task()));
|
|
6531
|
+
const failedResults = results.filter(
|
|
6532
|
+
(result) => result.status === "rejected"
|
|
6533
|
+
);
|
|
6534
|
+
if (failedResults.length > 0) {
|
|
6535
|
+
const firstFailure = failedResults[0];
|
|
6536
|
+
throw new Error(
|
|
6537
|
+
firstFailure.reason.message || "Error occurred during upload"
|
|
6538
|
+
);
|
|
6669
6539
|
}
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
position: absolute;
|
|
6673
|
-
top: -1px;
|
|
6674
|
-
left: -1px;
|
|
6675
|
-
right: -1px;
|
|
6676
|
-
bottom: -1px;
|
|
6677
|
-
border-radius: 32px;
|
|
6678
|
-
background: linear-gradient(135deg, rgba(255, 50, 50, 0.5), rgba(255, 150, 50, 0.5), rgba(150, 0, 50, 0.5));
|
|
6679
|
-
z-index: -1;
|
|
6680
|
-
opacity: 0.5;
|
|
6681
|
-
animation: borderGlow 3s ease-in-out infinite;
|
|
6540
|
+
if (hasFatalError) {
|
|
6541
|
+
throw new Error(fatalError || "Unknown error occurred during upload");
|
|
6682
6542
|
}
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6543
|
+
} catch (error) {
|
|
6544
|
+
throw fatalError ? new Error(fatalError) : error;
|
|
6545
|
+
}
|
|
6546
|
+
}
|
|
6547
|
+
async function completeChunkUpload(sessionId, deviceId, importAsCar = false) {
|
|
6548
|
+
var _a;
|
|
6549
|
+
try {
|
|
6550
|
+
const requestBody = { session_id: sessionId, uid: deviceId };
|
|
6551
|
+
const projectName = (_a = process.env.PINME_PROJECT_NAME) == null ? void 0 : _a.trim();
|
|
6552
|
+
let authHeaders = {};
|
|
6553
|
+
if (importAsCar) {
|
|
6554
|
+
requestBody.import_as_car = true;
|
|
6686
6555
|
}
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6556
|
+
if (projectName) {
|
|
6557
|
+
requestBody.project_name = projectName;
|
|
6558
|
+
authHeaders = getAuthHeaders();
|
|
6559
|
+
}
|
|
6560
|
+
console.log(
|
|
6561
|
+
`[chunk/complete] payload=${JSON.stringify({
|
|
6562
|
+
session_id: requestBody.session_id,
|
|
6563
|
+
uid: requestBody.uid,
|
|
6564
|
+
import_as_car: !!requestBody.import_as_car,
|
|
6565
|
+
project_name: requestBody.project_name || ""
|
|
6566
|
+
})}`
|
|
6567
|
+
);
|
|
6568
|
+
const response = await axios_default.post(
|
|
6569
|
+
`${IPFS_API_URL}/chunk/complete`,
|
|
6570
|
+
requestBody,
|
|
6571
|
+
{
|
|
6572
|
+
timeout: TIMEOUT,
|
|
6573
|
+
headers: {
|
|
6574
|
+
"Content-Type": "application/json",
|
|
6575
|
+
...authHeaders
|
|
6576
|
+
}
|
|
6577
|
+
}
|
|
6578
|
+
);
|
|
6579
|
+
const { code, msg, data } = response.data;
|
|
6580
|
+
if (code === 200 && data) {
|
|
6581
|
+
return data.trace_id;
|
|
6692
6582
|
}
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
60% { transform: translateX(-10px) rotate(-5deg); }
|
|
6698
|
-
80% { transform: translateX(10px) rotate(5deg); }
|
|
6583
|
+
throw new Error(`Complete upload failed: ${msg} (code: ${code})`);
|
|
6584
|
+
} catch (error) {
|
|
6585
|
+
if (axios_default.isAxiosError(error)) {
|
|
6586
|
+
throw new Error(`Network error: ${error.message}`);
|
|
6699
6587
|
}
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6588
|
+
throw error;
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
async function getChunkStatus(sessionId, deviceId) {
|
|
6592
|
+
try {
|
|
6593
|
+
const response = await axios_default.get(
|
|
6594
|
+
`${IPFS_API_URL}/up_status`,
|
|
6595
|
+
{
|
|
6596
|
+
params: { trace_id: sessionId, uid: deviceId },
|
|
6597
|
+
timeout: TIMEOUT,
|
|
6598
|
+
headers: { "Content-Type": "application/json" }
|
|
6599
|
+
}
|
|
6600
|
+
);
|
|
6601
|
+
const { code, msg, data } = response.data;
|
|
6602
|
+
if (code === 200) {
|
|
6603
|
+
return data;
|
|
6709
6604
|
}
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
padding: 1.25rem;
|
|
6715
|
-
background: rgba(255, 50, 50, 0.1);
|
|
6716
|
-
border-radius: 16px;
|
|
6717
|
-
border: 1px solid rgba(255, 50, 50, 0.2);
|
|
6718
|
-
font-weight: 500;
|
|
6719
|
-
box-shadow: 0 0 20px rgba(255, 50, 50, 0.1);
|
|
6605
|
+
throw new Error(`Server returned error: ${msg} (code: ${code})`);
|
|
6606
|
+
} catch (error) {
|
|
6607
|
+
if (axios_default.isAxiosError(error)) {
|
|
6608
|
+
throw new Error(`Network error: ${error.message}`);
|
|
6720
6609
|
}
|
|
6721
|
-
|
|
6722
|
-
</head>
|
|
6723
|
-
<body>
|
|
6724
|
-
<div class="bg"></div>
|
|
6725
|
-
<div class="grid"></div>
|
|
6726
|
-
<div class="container">
|
|
6727
|
-
<div class="error-icon">\u{1F635}</div>
|
|
6728
|
-
<h1>Oops!</h1>
|
|
6729
|
-
<div class="error">${error}</div>
|
|
6730
|
-
</div>
|
|
6731
|
-
</body>
|
|
6732
|
-
</html>`;
|
|
6610
|
+
throw error;
|
|
6733
6611
|
}
|
|
6734
|
-
}
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
const
|
|
6738
|
-
if (
|
|
6739
|
-
|
|
6612
|
+
}
|
|
6613
|
+
async function monitorChunkProgress(traceId, deviceId, progressBar) {
|
|
6614
|
+
let consecutiveErrors = 0;
|
|
6615
|
+
const startTime = Date.now();
|
|
6616
|
+
if (progressBar) {
|
|
6617
|
+
progressBar.startSimulatingProgress();
|
|
6740
6618
|
}
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
|
|
6619
|
+
try {
|
|
6620
|
+
while (Date.now() - startTime < MAX_POLL_TIME) {
|
|
6621
|
+
try {
|
|
6622
|
+
const status = await getChunkStatus(traceId, deviceId);
|
|
6623
|
+
consecutiveErrors = 0;
|
|
6624
|
+
if (status.is_ready && status.upload_rst.Hash) {
|
|
6625
|
+
if (progressBar) {
|
|
6626
|
+
progressBar.stopSimulatingProgress();
|
|
6627
|
+
}
|
|
6628
|
+
return {
|
|
6629
|
+
hash: status.upload_rst.Hash,
|
|
6630
|
+
shortUrl: status.upload_rst.ShortUrl
|
|
6631
|
+
};
|
|
6632
|
+
}
|
|
6633
|
+
} catch (error) {
|
|
6634
|
+
consecutiveErrors++;
|
|
6635
|
+
if (consecutiveErrors > 10) {
|
|
6636
|
+
throw new Error(`Polling failed: ${error.message}`);
|
|
6637
|
+
}
|
|
6638
|
+
}
|
|
6639
|
+
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL));
|
|
6640
|
+
}
|
|
6641
|
+
const maxPollTimeMinutes = Math.floor(MAX_POLL_TIME / (60 * 1e3));
|
|
6642
|
+
throw new Error(`Polling timeout after ${maxPollTimeMinutes} minutes`);
|
|
6643
|
+
} finally {
|
|
6644
|
+
if (progressBar) {
|
|
6645
|
+
progressBar.stopSimulatingProgress();
|
|
6646
|
+
}
|
|
6745
6647
|
}
|
|
6746
|
-
const config = { address, token };
|
|
6747
|
-
import_fs_extra5.default.ensureDirSync(CONFIG_DIR2);
|
|
6748
|
-
import_fs_extra5.default.writeJsonSync(AUTH_FILE2, config, { spaces: 2 });
|
|
6749
|
-
return config;
|
|
6750
6648
|
}
|
|
6751
|
-
function
|
|
6649
|
+
async function uploadDirectoryInChunks(directoryPath, deviceId, importAsCar = false) {
|
|
6650
|
+
const sizeCheck = checkDirectorySizeLimit(directoryPath);
|
|
6651
|
+
if (sizeCheck.exceeds) {
|
|
6652
|
+
throw new Error(
|
|
6653
|
+
`Directory ${directoryPath} exceeds size limit ${formatSize(
|
|
6654
|
+
sizeCheck.limit
|
|
6655
|
+
)} (size: ${formatSize(sizeCheck.size)})`
|
|
6656
|
+
);
|
|
6657
|
+
}
|
|
6658
|
+
const progressBar = new StepProgressBar(import_path6.default.basename(directoryPath), true);
|
|
6752
6659
|
try {
|
|
6753
|
-
|
|
6754
|
-
const
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6660
|
+
progressBar.startStep(0, "Preparing compression");
|
|
6661
|
+
const compressedPath = await compressDirectory(directoryPath);
|
|
6662
|
+
progressBar.completeStep();
|
|
6663
|
+
progressBar.startStep(1, "Initializing session");
|
|
6664
|
+
const sessionInfo = await initChunkSession(compressedPath, deviceId, true);
|
|
6665
|
+
progressBar.completeStep();
|
|
6666
|
+
progressBar.startStep(2, "Chunk upload");
|
|
6667
|
+
await uploadFileChunks(
|
|
6668
|
+
compressedPath,
|
|
6669
|
+
sessionInfo.session_id,
|
|
6670
|
+
sessionInfo.total_chunks,
|
|
6671
|
+
sessionInfo.chunk_size,
|
|
6672
|
+
deviceId,
|
|
6673
|
+
progressBar
|
|
6674
|
+
);
|
|
6675
|
+
progressBar.completeStep();
|
|
6676
|
+
progressBar.startStep(3, "Completing upload");
|
|
6677
|
+
const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
|
|
6678
|
+
progressBar.completeStep();
|
|
6679
|
+
progressBar.startStep(4, "Waiting for processing");
|
|
6680
|
+
const result = await monitorChunkProgress(traceId, deviceId, progressBar);
|
|
6681
|
+
progressBar.completeStep();
|
|
6682
|
+
try {
|
|
6683
|
+
import_fs_extra5.default.unlinkSync(compressedPath);
|
|
6684
|
+
} catch (error) {
|
|
6685
|
+
}
|
|
6686
|
+
const uploadData = {
|
|
6687
|
+
path: directoryPath,
|
|
6688
|
+
filename: import_path6.default.basename(directoryPath),
|
|
6689
|
+
contentHash: (result == null ? void 0 : result.hash) || "unknown",
|
|
6690
|
+
size: sizeCheck.size,
|
|
6691
|
+
fileCount: 0,
|
|
6692
|
+
isDirectory: true,
|
|
6693
|
+
shortUrl: (result == null ? void 0 : result.shortUrl) || null
|
|
6694
|
+
};
|
|
6695
|
+
saveUploadHistory(uploadData);
|
|
6696
|
+
if (!(result == null ? void 0 : result.hash)) {
|
|
6697
|
+
throw new Error("Server did not return valid hash value");
|
|
6698
|
+
}
|
|
6699
|
+
progressBar.complete();
|
|
6700
|
+
return result;
|
|
6701
|
+
} catch (error) {
|
|
6702
|
+
progressBar.fail(error.message);
|
|
6703
|
+
throw error;
|
|
6759
6704
|
}
|
|
6760
6705
|
}
|
|
6761
|
-
function
|
|
6706
|
+
async function uploadFileInChunks(filePath, deviceId, importAsCar = false) {
|
|
6707
|
+
const sizeCheck = checkFileSizeLimit(filePath);
|
|
6708
|
+
if (sizeCheck.exceeds) {
|
|
6709
|
+
throw new Error(
|
|
6710
|
+
`File ${filePath} exceeds size limit ${formatSize(
|
|
6711
|
+
sizeCheck.limit
|
|
6712
|
+
)} (size: ${formatSize(sizeCheck.size)})`
|
|
6713
|
+
);
|
|
6714
|
+
}
|
|
6715
|
+
const fileName = import_path6.default.basename(filePath);
|
|
6716
|
+
const progressBar = new StepProgressBar(fileName, false);
|
|
6762
6717
|
try {
|
|
6763
|
-
|
|
6764
|
-
|
|
6718
|
+
progressBar.startStep(0, "Initializing session");
|
|
6719
|
+
const sessionInfo = await initChunkSession(filePath, deviceId, false);
|
|
6720
|
+
progressBar.completeStep();
|
|
6721
|
+
progressBar.startStep(1, "Chunk upload");
|
|
6722
|
+
await uploadFileChunks(
|
|
6723
|
+
filePath,
|
|
6724
|
+
sessionInfo.session_id,
|
|
6725
|
+
sessionInfo.total_chunks,
|
|
6726
|
+
sessionInfo.chunk_size,
|
|
6727
|
+
deviceId,
|
|
6728
|
+
progressBar
|
|
6729
|
+
);
|
|
6730
|
+
progressBar.completeStep();
|
|
6731
|
+
progressBar.startStep(2, "Completing upload");
|
|
6732
|
+
const traceId = await completeChunkUpload(sessionInfo.session_id, deviceId, importAsCar);
|
|
6733
|
+
progressBar.completeStep();
|
|
6734
|
+
progressBar.startStep(3, "Waiting for processing");
|
|
6735
|
+
const result = await monitorChunkProgress(traceId, deviceId, progressBar);
|
|
6736
|
+
progressBar.completeStep();
|
|
6737
|
+
const uploadData = {
|
|
6738
|
+
path: filePath,
|
|
6739
|
+
filename: fileName,
|
|
6740
|
+
contentHash: (result == null ? void 0 : result.hash) || "unknown",
|
|
6741
|
+
previewHash: null,
|
|
6742
|
+
size: sizeCheck.size,
|
|
6743
|
+
fileCount: 1,
|
|
6744
|
+
isDirectory: false,
|
|
6745
|
+
shortUrl: (result == null ? void 0 : result.shortUrl) || null
|
|
6746
|
+
};
|
|
6747
|
+
saveUploadHistory(uploadData);
|
|
6748
|
+
if (!(result == null ? void 0 : result.hash)) {
|
|
6749
|
+
throw new Error("Server did not return valid hash value");
|
|
6765
6750
|
}
|
|
6751
|
+
progressBar.complete();
|
|
6752
|
+
return result;
|
|
6766
6753
|
} catch (error) {
|
|
6767
|
-
|
|
6754
|
+
progressBar.fail(error.message);
|
|
6755
|
+
throw error;
|
|
6768
6756
|
}
|
|
6769
6757
|
}
|
|
6770
|
-
function
|
|
6771
|
-
const
|
|
6772
|
-
if (!
|
|
6773
|
-
throw new Error("
|
|
6758
|
+
async function uploadToIpfsSplit_default(filePath, importAsCar = false) {
|
|
6759
|
+
const deviceId = getUid();
|
|
6760
|
+
if (!deviceId) {
|
|
6761
|
+
throw new Error("Device ID not found");
|
|
6762
|
+
}
|
|
6763
|
+
try {
|
|
6764
|
+
const isDirectory = import_fs_extra5.default.statSync(filePath).isDirectory();
|
|
6765
|
+
const result = isDirectory ? await uploadDirectoryInChunks(filePath, deviceId, importAsCar) : await uploadFileInChunks(filePath, deviceId, importAsCar);
|
|
6766
|
+
if (result == null ? void 0 : result.hash) {
|
|
6767
|
+
return {
|
|
6768
|
+
contentHash: result.hash,
|
|
6769
|
+
previewHash: null,
|
|
6770
|
+
shortUrl: result.shortUrl
|
|
6771
|
+
};
|
|
6772
|
+
}
|
|
6773
|
+
return null;
|
|
6774
|
+
} catch (error) {
|
|
6775
|
+
return null;
|
|
6774
6776
|
}
|
|
6775
|
-
return {
|
|
6776
|
-
"token-address": conf.address,
|
|
6777
|
-
"authentication-tokens": conf.token
|
|
6778
|
-
};
|
|
6779
6777
|
}
|
|
6780
6778
|
|
|
6779
|
+
// bin/upload.ts
|
|
6780
|
+
var import_fs2 = __toESM(require("fs"));
|
|
6781
|
+
var import_crypto_js = __toESM(require("crypto-js"));
|
|
6782
|
+
|
|
6781
6783
|
// bin/utils/pinmeApi.ts
|
|
6784
|
+
var import_chalk4 = __toESM(require("chalk"));
|
|
6782
6785
|
var DEFAULT_BASE = "https://pinme.benny1996.win/api/v4";
|
|
6783
6786
|
var TOKEN_EXPIRED_CODES = [
|
|
6784
6787
|
401,
|
|
@@ -9116,10 +9119,9 @@ async function updateWebCmd(options) {
|
|
|
9116
9119
|
var import_chalk21 = __toESM(require("chalk"));
|
|
9117
9120
|
var import_inquirer9 = __toESM(require("inquirer"));
|
|
9118
9121
|
var import_fs_extra11 = __toESM(require("fs-extra"));
|
|
9119
|
-
var import_path16 = __toESM(require("path"));
|
|
9120
9122
|
var API_BASE5 = "https://pinme.benny1996.win/api/v4";
|
|
9121
9123
|
function getProjectName() {
|
|
9122
|
-
const configPath =
|
|
9124
|
+
const configPath = path.join(process.cwd(), "pinme.toml");
|
|
9123
9125
|
if (!import_fs_extra11.default.existsSync(configPath)) {
|
|
9124
9126
|
return null;
|
|
9125
9127
|
}
|
|
@@ -9188,14 +9190,7 @@ Project: ${data.data.project_name}`));
|
|
|
9188
9190
|
console.log(import_chalk21.default.gray(` Domain deleted: ${data.data.domain_deleted ? "\u2705" : "\u274C"}`));
|
|
9189
9191
|
console.log(import_chalk21.default.gray(` Worker deleted: ${data.data.worker_deleted ? "\u2705" : "\u274C"}`));
|
|
9190
9192
|
console.log(import_chalk21.default.gray(` Database deleted: ${data.data.database_deleted ? "\u2705" : "\u274C"}`));
|
|
9191
|
-
|
|
9192
|
-
if (import_fs_extra11.default.existsSync(projectDir)) {
|
|
9193
|
-
console.log(import_chalk21.default.blue("\nDeleting local project directory..."));
|
|
9194
|
-
const parentDir = import_path16.default.dirname(projectDir);
|
|
9195
|
-
process.chdir(parentDir);
|
|
9196
|
-
await import_fs_extra11.default.remove(projectDir);
|
|
9197
|
-
console.log(import_chalk21.default.green(`\u2705 Local directory deleted: ${projectDir}`));
|
|
9198
|
-
}
|
|
9193
|
+
console.log(import_chalk21.default.gray("\nLocal files are kept unchanged."));
|
|
9199
9194
|
} else {
|
|
9200
9195
|
const errorMsg = (data == null ? void 0 : data.msg) || "Failed to delete project";
|
|
9201
9196
|
throw new Error(errorMsg);
|