easy-vps 0.1.7 → 0.1.9

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.
@@ -237,13 +237,16 @@ function buildScript(config) {
237
237
  `mkdir -p ${APPS_DIR}`,
238
238
  // Embed the stored Github token straight into the clone URL so the fetch
239
239
  // authenticates without depending on the credential helper being picked up
240
- // inside this streaming login shell. Falls back to the plain URL (credential
241
- // helper still applies) when no token is configured.
240
+ // inside this streaming login shell. Prefers the panel's state file, then
241
+ // git's credential store, then falls back to the plain URL.
242
242
  `REPO=${(0, ssh_1.quote)(config.repository)}`,
243
243
  step('Resolving Github credentials'),
244
244
  'TOKEN=""\n' +
245
- 'if [ -f ~/.git-credentials ]; then\n' +
246
- ' TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -n1 | sed -E "s#^https://([^@]+)@github.com/?$#\\1#" | sed -E "s#^[^:]+:##")\n' +
245
+ 'if [ -f ~/.easy-vps/github.json ]; then\n' +
246
+ ' TOKEN=$(grep -o \'"token"[[:space:]]*:[[:space:]]*"[^"]*"\' ~/.easy-vps/github.json | head -n1 | sed -E \'s/.*:"([^"]*)"/\\1/\')\n' +
247
+ 'fi\n' +
248
+ 'if [ -z "$TOKEN" ] && [ -f ~/.git-credentials ]; then\n' +
249
+ ' TOKEN=$(grep "github.com" ~/.git-credentials 2>/dev/null | head -n1 | sed -E "s#^https://([^@]+)@github.com#\\1#" | sed -E "s#^[^:]+:##")\n' +
247
250
  'fi\n' +
248
251
  'if [ -n "$TOKEN" ]; then\n' +
249
252
  ' AUTH_REPO="https://${TOKEN}@github.com/${REPO#https://github.com/}"\n' +
@@ -4,7 +4,7 @@ export interface GithubConfig {
4
4
  email: string;
5
5
  /** The stored personal access token; returned so the UI can prefill it. */
6
6
  personalAccessToken: string;
7
- /** True when a token is already stored in the credential store on the server. */
7
+ /** True when a token is already stored on the server. */
8
8
  tokenSet: boolean;
9
9
  }
10
10
  /** Only the fields the user may change; each is optional on update. */
@@ -17,7 +17,8 @@ export interface GithubConfigPatch {
17
17
  export declare function getGithubConfig(remote: Remote): Promise<GithubConfig>;
18
18
  /**
19
19
  * Applies the supplied github identity over SSH and stores the personal access
20
- * token in git's credential store so HTTPS clones can authenticate. Returns the
21
- * resulting config (without the token, which is never sent back).
20
+ * token in git's credential store (so HTTPS clones authenticate) and in the
21
+ * panel's state file (the reliable source of truth for display and deploys).
22
+ * Returns the resulting config.
22
23
  */
23
24
  export declare function updateGithubConfig(remote: Remote, patch: GithubConfigPatch): Promise<GithubConfig>;
@@ -4,18 +4,70 @@ exports.getGithubConfig = getGithubConfig;
4
4
  exports.updateGithubConfig = updateGithubConfig;
5
5
  // Github identity and credential configuration on the managed VPS, over SSH.
6
6
  const ssh_1 = require("./ssh");
7
+ const STATE_DIR = '.easy-vps';
8
+ const STATE_FILE = 'github.json';
7
9
  function safeTrim(value) {
8
10
  return typeof value === 'string' ? value.trim() : '';
9
11
  }
12
+ /** Resolves the login user's home directory on the remote. */
13
+ async function homeDir(remote) {
14
+ const { stdout } = await remote.exec('echo $HOME', 10_000);
15
+ return stdout.trim() || '';
16
+ }
17
+ /** Path to the panel-owned state file holding the github config. */
18
+ async function statePath(remote) {
19
+ const home = await homeDir(remote);
20
+ return `${home}/${STATE_DIR}/${STATE_FILE}`;
21
+ }
22
+ /** Reads the panel's own state file; null when it has never been written. */
23
+ async function readState(remote) {
24
+ const path = await statePath(remote);
25
+ if (!(await remote.exists(path)))
26
+ return null;
27
+ try {
28
+ const parsed = JSON.parse(await remote.readFile(path));
29
+ return {
30
+ username: parsed.username ?? '',
31
+ email: parsed.email ?? '',
32
+ token: parsed.token ?? '',
33
+ };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /** Persists the github config to the panel's state file (written via tee). */
40
+ async function writeState(remote, username, email, token) {
41
+ const home = await homeDir(remote);
42
+ const dir = `${home}/${STATE_DIR}`;
43
+ const path = `${dir}/${STATE_FILE}`;
44
+ await remote.exec(`mkdir -p ${(0, ssh_1.quote)(dir)}`);
45
+ await remote.writeFile(path, JSON.stringify({ username, email, token }));
46
+ }
47
+ /** Extracts a stored token from git's credential store, if present. */
48
+ async function tokenFromCredentials(remote) {
49
+ const result = await remote.exec((0, ssh_1.loginShell)("cred=$(grep 'github.com' ~/.git-credentials 2>/dev/null | head -n1); " +
50
+ "if [ -n \"$cred\" ]; then " +
51
+ "printf '%s' \"$cred\" | sed -E 's#^https://([^@]+)@github.com#\\1#' | sed -E 's#^[^:]+:##'; " +
52
+ "fi"), 10_000);
53
+ return result.stdout.trim();
54
+ }
10
55
  /** Reads the git identity currently configured for the remote user. */
11
56
  async function getGithubConfig(remote) {
57
+ // Prefer the panel's state file so display always reflects what was saved.
58
+ const state = await readState(remote);
59
+ if (state) {
60
+ return {
61
+ username: state.username,
62
+ email: state.email,
63
+ personalAccessToken: state.token,
64
+ tokenSet: state.token !== '',
65
+ };
66
+ }
67
+ // Fall back to git config + the credential store for partial setups.
12
68
  const nameResult = await remote.exec((0, ssh_1.loginShell)('git config --global --get user.name 2>/dev/null'), 10_000);
13
69
  const emailResult = await remote.exec((0, ssh_1.loginShell)('git config --global --get user.email 2>/dev/null'), 10_000);
14
- const tokenResult = await remote.exec((0, ssh_1.loginShell)("cred=$(grep 'github.com' ~/.git-credentials 2>/dev/null | head -n1); " +
15
- "if [ -n \"$cred\" ]; then " +
16
- "printf '%s' \"$cred\" | sed -E 's#^https://([^@]+)@github.com/?$#\\1#' | sed -E 's#^[^:]+:##'; " +
17
- "fi"), 10_000);
18
- const token = tokenResult.stdout.trim();
70
+ const token = await tokenFromCredentials(remote);
19
71
  return {
20
72
  username: nameResult.stdout.trim(),
21
73
  email: emailResult.stdout.trim(),
@@ -25,13 +77,15 @@ async function getGithubConfig(remote) {
25
77
  }
26
78
  /**
27
79
  * Applies the supplied github identity over SSH and stores the personal access
28
- * token in git's credential store so HTTPS clones can authenticate. Returns the
29
- * resulting config (without the token, which is never sent back).
80
+ * token in git's credential store (so HTTPS clones authenticate) and in the
81
+ * panel's state file (the reliable source of truth for display and deploys).
82
+ * Returns the resulting config.
30
83
  */
31
84
  async function updateGithubConfig(remote, patch) {
32
- const username = safeTrim(patch.username);
33
- const email = safeTrim(patch.email);
34
- const token = safeTrim(patch.personalAccessToken);
85
+ const existing = await getGithubConfig(remote);
86
+ const username = patch.username !== undefined ? safeTrim(patch.username) : existing.username;
87
+ const email = patch.email !== undefined ? safeTrim(patch.email) : existing.email;
88
+ const token = patch.personalAccessToken !== undefined ? safeTrim(patch.personalAccessToken) : existing.personalAccessToken;
35
89
  if (patch.username !== undefined && username === '') {
36
90
  throw new Error('Github username is required.');
37
91
  }
@@ -59,10 +113,14 @@ async function updateGithubConfig(remote, patch) {
59
113
  `printf '%s\\n' ${(0, ssh_1.quote)(url)} >> ~/.git-credentials; ` +
60
114
  `chmod 600 ~/.git-credentials`);
61
115
  }
62
- const script = (0, ssh_1.loginShell)(commands.join('\n'));
63
- const { code, stderr } = await remote.exec(script, 30_000);
64
- if (code !== 0) {
65
- throw new Error(`Failed to apply github configuration: ${stderr.trim()}`);
116
+ if (commands.length > 0) {
117
+ const script = (0, ssh_1.loginShell)(commands.join('\n'));
118
+ const { code, stderr } = await remote.exec(script, 30_000);
119
+ if (code !== 0) {
120
+ throw new Error(`Failed to apply github configuration: ${stderr.trim()}`);
121
+ }
66
122
  }
123
+ // Always persist the merged config to the panel's state file.
124
+ await writeState(remote, username, email, token);
67
125
  return getGithubConfig(remote);
68
126
  }
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "easy-vps",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Install dependencies, point domains, deploy apps and run Postgres on a VPS from one UI",
5
5
  "keywords": [
6
+ "easy-vps",
6
7
  "vps",
7
8
  "nginx",
8
9
  "pm2",