@naturalcycles/backend-lib 9.79.0 → 9.81.0
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/deploy/deployHealthCheck.d.ts +1 -5
- package/dist/deploy/deployHealthCheck.js +3 -34
- package/dist/deploy/index.d.ts +2 -9
- package/dist/deploy/index.js +1 -6
- package/dist/express/createDefaultApp.d.ts +23 -0
- package/dist/express/createDefaultApp.js +5 -3
- package/dist/server/serverStatusMiddleware.d.ts +2 -2
- package/dist/server/serverStatusMiddleware.js +5 -14
- package/dist/util.d.ts +0 -1
- package/dist/util.js +1 -4
- package/package.json +3 -13
- package/src/admin/login.html +103 -76
- package/src/deploy/deployHealthCheck.ts +2 -48
- package/src/deploy/index.ts +2 -17
- package/src/express/createDefaultApp.ts +35 -3
- package/src/server/serverStatusMiddleware.ts +4 -20
- package/src/util.ts +1 -5
- package/bin/deploy-gae.js +0 -2
- package/bin/deploy-prepare.js +0 -2
- package/bin/undeploy-gae.js +0 -2
- package/dist/bin/deploy-gae.d.ts +0 -1
- package/dist/bin/deploy-gae.js +0 -12
- package/dist/bin/deploy-prepare.d.ts +0 -1
- package/dist/bin/deploy-prepare.js +0 -19
- package/dist/bin/undeploy-gae.d.ts +0 -1
- package/dist/bin/undeploy-gae.js +0 -13
- package/dist/deploy/backend.cfg.util.d.ts +0 -30
- package/dist/deploy/backend.cfg.util.js +0 -23
- package/dist/deploy/deploy.model.d.ts +0 -29
- package/dist/deploy/deploy.model.js +0 -1
- package/dist/deploy/deploy.util.d.ts +0 -7
- package/dist/deploy/deploy.util.js +0 -151
- package/dist/deploy/deployGae.d.ts +0 -10
- package/dist/deploy/deployGae.js +0 -90
- package/dist/deploy/deployInfo.util.d.ts +0 -1
- package/dist/deploy/deployInfo.util.js +0 -25
- package/dist/deploy/deployPrepare.d.ts +0 -13
- package/dist/deploy/deployPrepare.js +0 -85
- package/src/bin/deploy-gae.ts +0 -14
- package/src/bin/deploy-prepare.ts +0 -24
- package/src/bin/undeploy-gae.ts +0 -15
- package/src/deploy/backend.cfg.util.ts +0 -63
- package/src/deploy/deploy.model.ts +0 -32
- package/src/deploy/deploy.util.ts +0 -214
- package/src/deploy/deployGae.ts +0 -127
- package/src/deploy/deployInfo.util.ts +0 -27
- package/src/deploy/deployPrepare.ts +0 -113
- package/src/deploy/files-default/.gcloudignore +0 -7
|
@@ -4,11 +4,6 @@ export interface DeployHealthCheckOptions {
|
|
|
4
4
|
maxTries?: number;
|
|
5
5
|
timeoutSec?: number;
|
|
6
6
|
intervalSec?: number;
|
|
7
|
-
logOnFailure?: boolean;
|
|
8
|
-
logOnSuccess?: boolean;
|
|
9
|
-
gaeProject?: string;
|
|
10
|
-
gaeService?: string;
|
|
11
|
-
gaeVersion?: string;
|
|
12
7
|
authHeader?: string;
|
|
13
8
|
}
|
|
14
9
|
export declare const deployHealthCheckCliOptions: CliOptions;
|
|
@@ -16,5 +11,6 @@ export declare const deployHealthCheckCliOptions: CliOptions;
|
|
|
16
11
|
* Requires up to `healthyThreshold` consecutive OK responses to succeed.
|
|
17
12
|
* Fails on up to `unhealthyThreshold` consecutive FAIL responses.
|
|
18
13
|
* Fails after maxTries.
|
|
14
|
+
* Throws on failure.
|
|
19
15
|
*/
|
|
20
16
|
export declare function deployHealthCheck(url: string, opt?: DeployHealthCheckOptions): Promise<void>;
|
|
@@ -4,7 +4,6 @@ import { getFetcher } from '@naturalcycles/js-lib/http';
|
|
|
4
4
|
import { _filterFalsyValues, _filterUndefinedValues, } from '@naturalcycles/js-lib/object/object.util.js';
|
|
5
5
|
import { pDelay } from '@naturalcycles/js-lib/promise/pDelay.js';
|
|
6
6
|
import { dimGrey, red } from '@naturalcycles/nodejs-lib/colors';
|
|
7
|
-
import { exec2 } from '@naturalcycles/nodejs-lib/exec2';
|
|
8
7
|
import { coloredHttpCode } from '../server/request.log.util.js';
|
|
9
8
|
export const deployHealthCheckCliOptions = {
|
|
10
9
|
thresholdHealthy: {
|
|
@@ -27,25 +26,6 @@ export const deployHealthCheckCliOptions = {
|
|
|
27
26
|
type: 'number',
|
|
28
27
|
default: 1,
|
|
29
28
|
},
|
|
30
|
-
logOnFailure: {
|
|
31
|
-
type: 'boolean',
|
|
32
|
-
default: true,
|
|
33
|
-
desc: 'Show server logs on health check failure (requires gaeProject, gaeService, gaeVersion)',
|
|
34
|
-
},
|
|
35
|
-
logOnSuccess: {
|
|
36
|
-
type: 'boolean',
|
|
37
|
-
default: false,
|
|
38
|
-
desc: 'Show server logs on health check success (requires gaeProject, gaeService, gaeVersion)',
|
|
39
|
-
},
|
|
40
|
-
gaeProject: {
|
|
41
|
-
type: 'string',
|
|
42
|
-
},
|
|
43
|
-
gaeService: {
|
|
44
|
-
type: 'string',
|
|
45
|
-
},
|
|
46
|
-
gaeVersion: {
|
|
47
|
-
type: 'string',
|
|
48
|
-
},
|
|
49
29
|
authHeader: {
|
|
50
30
|
type: 'string',
|
|
51
31
|
},
|
|
@@ -58,9 +38,10 @@ const inspectOpt = {
|
|
|
58
38
|
* Requires up to `healthyThreshold` consecutive OK responses to succeed.
|
|
59
39
|
* Fails on up to `unhealthyThreshold` consecutive FAIL responses.
|
|
60
40
|
* Fails after maxTries.
|
|
41
|
+
* Throws on failure.
|
|
61
42
|
*/
|
|
62
43
|
export async function deployHealthCheck(url, opt = {}) {
|
|
63
|
-
const { thresholdHealthy = 5, thresholdUnhealthy = 8, maxTries = 30, timeoutSec = 30, intervalSec = 1,
|
|
44
|
+
const { thresholdHealthy = 5, thresholdUnhealthy = 8, maxTries = 30, timeoutSec = 30, intervalSec = 1, authHeader, } = opt;
|
|
64
45
|
let attempt = 0;
|
|
65
46
|
let countHealthy = 0;
|
|
66
47
|
let countUnhealthy = 0;
|
|
@@ -74,19 +55,7 @@ export async function deployHealthCheck(url, opt = {}) {
|
|
|
74
55
|
}
|
|
75
56
|
if (failed) {
|
|
76
57
|
console.log(red(`Health check failed!`));
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
exec2.spawn(`gcloud app logs read --project ${gaeProject} --service ${gaeService} --version ${gaeVersion}`);
|
|
80
|
-
}
|
|
81
|
-
catch { }
|
|
82
|
-
}
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
if (logOnSuccess) {
|
|
86
|
-
try {
|
|
87
|
-
exec2.spawn(`gcloud app logs read --project ${gaeProject} --service ${gaeService} --version ${gaeVersion}`);
|
|
88
|
-
}
|
|
89
|
-
catch { }
|
|
58
|
+
throw new Error(`Health check failed: ${doneReason}`);
|
|
90
59
|
}
|
|
91
60
|
async function makeAttempt() {
|
|
92
61
|
attempt++;
|
package/dist/deploy/index.d.ts
CHANGED
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import type { BackendCfg } from './backend.cfg.util.js';
|
|
2
|
-
import { getBackendCfg } from './backend.cfg.util.js';
|
|
3
|
-
import type { DeployInfo } from './deploy.model.js';
|
|
4
|
-
import { createAppYaml, createDeployInfo } from './deploy.util.js';
|
|
5
|
-
import { deployGae } from './deployGae.js';
|
|
6
1
|
import type { DeployHealthCheckOptions } from './deployHealthCheck.js';
|
|
7
2
|
import { deployHealthCheck } from './deployHealthCheck.js';
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
export { createAppYaml, createDeployInfo, deployGae, deployHealthCheck, deployPrepare, getBackendCfg, };
|
|
11
|
-
export * from './deployInfo.util.js';
|
|
3
|
+
export type { DeployHealthCheckOptions };
|
|
4
|
+
export { deployHealthCheck };
|
package/dist/deploy/index.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
import { getBackendCfg } from './backend.cfg.util.js';
|
|
2
|
-
import { createAppYaml, createDeployInfo } from './deploy.util.js';
|
|
3
|
-
import { deployGae } from './deployGae.js';
|
|
4
1
|
import { deployHealthCheck } from './deployHealthCheck.js';
|
|
5
|
-
|
|
6
|
-
export { createAppYaml, createDeployInfo, deployGae, deployHealthCheck, deployPrepare, getBackendCfg, };
|
|
7
|
-
export * from './deployInfo.util.js';
|
|
2
|
+
export { deployHealthCheck };
|
|
@@ -32,4 +32,27 @@ export interface DefaultAppCfg {
|
|
|
32
32
|
bodyParserRawOptions?: Options;
|
|
33
33
|
corsOptions?: CorsOptions;
|
|
34
34
|
genericErrorMwCfg?: GenericErrorMiddlewareCfg;
|
|
35
|
+
/**
|
|
36
|
+
* Value for Express `trust proxy` setting, which defines how `req.ip`, `req.ips`,
|
|
37
|
+
* `req.protocol` and `req.hostname` are resolved from `X-Forwarded-*` headers.
|
|
38
|
+
*
|
|
39
|
+
* Defaults to `2` - trust exactly 2 hops: the socket peer (Google Front End) and the
|
|
40
|
+
* rightmost `X-Forwarded-For` entry (appended by the Google Cloud Load Balancer).
|
|
41
|
+
* This is the setup of Cloud Run behind a Load Balancer.
|
|
42
|
+
* Any further entries to the left could be injected by the client, so they are not trusted,
|
|
43
|
+
* which prevents IP address spoofing via `X-Forwarded-For` header.
|
|
44
|
+
*
|
|
45
|
+
* Do NOT set it to `true` in production - it trusts the leftmost `X-Forwarded-For` entry,
|
|
46
|
+
* which is fully client-controlled.
|
|
47
|
+
*
|
|
48
|
+
* Adjust it to match the hosting environment (number of proxies in front of the app).
|
|
49
|
+
*
|
|
50
|
+
* @default 2
|
|
51
|
+
*/
|
|
52
|
+
trustProxy?: TrustProxy;
|
|
35
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Accepted values of Express `trust proxy` setting.
|
|
56
|
+
* See https://expressjs.com/en/guide/behind-proxies.html
|
|
57
|
+
*/
|
|
58
|
+
export type TrustProxy = boolean | number | string | string[] | ((ip: string | undefined, hopIndex: number) => boolean);
|
|
@@ -5,9 +5,10 @@ import { logMiddleware } from '../server/logMiddleware.js';
|
|
|
5
5
|
import { notFoundMiddleware } from '../server/notFoundMiddleware.js';
|
|
6
6
|
import { requestTimeoutMiddleware } from '../server/requestTimeoutMiddleware.js';
|
|
7
7
|
import { simpleRequestLoggerMiddleware } from '../server/simpleRequestLoggerMiddleware.js';
|
|
8
|
-
import {
|
|
8
|
+
import { isCloudRun } from '../util.js';
|
|
9
9
|
const isTest = process.env['APP_ENV'] === 'test';
|
|
10
10
|
const isDev = process.env['APP_ENV'] === 'dev';
|
|
11
|
+
const isGAE = !!process.env['GAE_INSTANCE'];
|
|
11
12
|
export async function createDefaultApp(cfg) {
|
|
12
13
|
const { sentryService } = cfg;
|
|
13
14
|
const { default: express } = await import('express');
|
|
@@ -16,7 +17,7 @@ export async function createDefaultApp(cfg) {
|
|
|
16
17
|
const app = express();
|
|
17
18
|
app.disable('etag');
|
|
18
19
|
app.disable('x-powered-by');
|
|
19
|
-
app.set('trust proxy',
|
|
20
|
+
app.set('trust proxy', cfg.trustProxy ?? 2);
|
|
20
21
|
// preHandlers
|
|
21
22
|
useHandlers(app, cfg.preHandlers);
|
|
22
23
|
app.use(logMiddleware());
|
|
@@ -43,8 +44,9 @@ export async function createDefaultApp(cfg) {
|
|
|
43
44
|
maxAge: 86400,
|
|
44
45
|
...cfg.corsOptions,
|
|
45
46
|
}));
|
|
46
|
-
if (!isGAE
|
|
47
|
+
if (!isGAE && (isTest || isCloudRun())) {
|
|
47
48
|
// compression is not used in AppEngine, because AppEngine provides it by default
|
|
49
|
+
// Cloud Run doesn't compress responses, so we do it ourselves
|
|
48
50
|
app.use(compressionMiddleware());
|
|
49
51
|
}
|
|
50
52
|
// app.use(safeJsonMiddleware()) // optional
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { BackendRequestHandler } from './server.model.js';
|
|
2
|
-
export declare function serverStatusMiddleware(
|
|
3
|
-
export declare function getServerStatusData(
|
|
2
|
+
export declare function serverStatusMiddleware(extra?: any): BackendRequestHandler;
|
|
3
|
+
export declare function getServerStatusData(extra?: any): Record<string, any>;
|
|
@@ -2,31 +2,22 @@ import os from 'node:os';
|
|
|
2
2
|
import { localTime } from '@naturalcycles/js-lib/datetime/localTime.js';
|
|
3
3
|
import { _filterNullishValues } from '@naturalcycles/js-lib/object/object.util.js';
|
|
4
4
|
import { memoryUsageFull, processSharedUtil } from '@naturalcycles/nodejs-lib';
|
|
5
|
-
import { getDeployInfo } from '../deploy/deployInfo.util.js';
|
|
6
5
|
const { versions, arch, platform } = process;
|
|
7
6
|
const availableParallelismAtStart = os.availableParallelism();
|
|
8
|
-
const {
|
|
9
|
-
export function serverStatusMiddleware(
|
|
7
|
+
const { GOOGLE_CLOUD_PROJECT, K_SERVICE, K_REVISION, APP_ENV, NODE_ENV, NODE_OPTIONS, UV_THREADPOOL_SIZE, MALLOC_ARENA_MAX, DEPLOY_BUILD_TIME, BUILD_VERSION, } = process.env;
|
|
8
|
+
export function serverStatusMiddleware(extra) {
|
|
10
9
|
return async (_req, res) => {
|
|
11
|
-
res.json(getServerStatusData(
|
|
10
|
+
res.json(getServerStatusData(extra));
|
|
12
11
|
};
|
|
13
12
|
}
|
|
14
|
-
export function getServerStatusData(
|
|
15
|
-
let deployBuildTime = DEPLOY_BUILD_TIME;
|
|
16
|
-
if (!deployBuildTime) {
|
|
17
|
-
const { ts } = getDeployInfo(projectDir);
|
|
18
|
-
deployBuildTime = localTime(ts).toPretty();
|
|
19
|
-
}
|
|
13
|
+
export function getServerStatusData(extra) {
|
|
20
14
|
const availableParallelism = os.availableParallelism();
|
|
21
15
|
return _filterNullishValues({
|
|
22
16
|
nodeProcessStarted: getStartedStr(),
|
|
23
|
-
|
|
17
|
+
DEPLOY_BUILD_TIME,
|
|
24
18
|
BUILD_VERSION,
|
|
25
19
|
APP_ENV,
|
|
26
20
|
GOOGLE_CLOUD_PROJECT,
|
|
27
|
-
GAE_APPLICATION,
|
|
28
|
-
GAE_SERVICE,
|
|
29
|
-
GAE_VERSION,
|
|
30
21
|
K_SERVICE,
|
|
31
22
|
K_REVISION,
|
|
32
23
|
processInfo: {
|
package/dist/util.d.ts
CHANGED
package/dist/util.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/backend-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "9.
|
|
4
|
+
"version": "9.81.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/db-lib": "^10",
|
|
7
7
|
"@naturalcycles/js-lib": "^15",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@sentry/node-core": "^10",
|
|
23
|
-
"firebase-admin": "^14"
|
|
24
|
-
"simple-git": "^4"
|
|
23
|
+
"firebase-admin": "^14"
|
|
25
24
|
},
|
|
26
25
|
"peerDependenciesMeta": {
|
|
27
26
|
"@sentry/node-core": {
|
|
@@ -29,9 +28,6 @@
|
|
|
29
28
|
},
|
|
30
29
|
"firebase-admin": {
|
|
31
30
|
"optional": true
|
|
32
|
-
},
|
|
33
|
-
"simple-git": {
|
|
34
|
-
"optional": true
|
|
35
31
|
}
|
|
36
32
|
},
|
|
37
33
|
"devDependencies": {
|
|
@@ -68,9 +64,6 @@
|
|
|
68
64
|
"!src/**/__exclude"
|
|
69
65
|
],
|
|
70
66
|
"bin": {
|
|
71
|
-
"deploy-gae": "bin/deploy-gae.js",
|
|
72
|
-
"undeploy-gae": "bin/undeploy-gae.js",
|
|
73
|
-
"deploy-prepare": "bin/deploy-prepare.js",
|
|
74
67
|
"deploy-health-check": "bin/deploy-health-check.js"
|
|
75
68
|
},
|
|
76
69
|
"types": "dist/index.d.ts",
|
|
@@ -85,7 +78,7 @@
|
|
|
85
78
|
"engines": {
|
|
86
79
|
"node": ">=24.10.0"
|
|
87
80
|
},
|
|
88
|
-
"description": "Standard library for making Express.js
|
|
81
|
+
"description": "Standard library for making Express.js based backend services",
|
|
89
82
|
"author": "Natural Cycles Team",
|
|
90
83
|
"license": "MIT",
|
|
91
84
|
"scripts": {
|
|
@@ -97,9 +90,6 @@
|
|
|
97
90
|
"check": "dev-lib check",
|
|
98
91
|
"typecheck": "dev-lib typecheck",
|
|
99
92
|
"dev": "APP_ENV=dev tsx watch src/test/server/server.ts",
|
|
100
|
-
"deploy-gae": "tsx src/bin/deploy-gae.ts",
|
|
101
|
-
"deploy-prepare": "tsx src/bin/deploy-prepare.ts",
|
|
102
|
-
"deploy-prepare-debug": "AA=AA1 BB=BB1 tsx src/bin/deploy-prepare.ts --projectDir ./src/test/project",
|
|
103
93
|
"deploy-health-check-debug1": "tsx src/bin/deploy-health-check.ts --url https://api-master.naturalcycles.com",
|
|
104
94
|
"deploy-health-check-debug2": "tsx src/bin/deploy-health-check.ts --url https://api-master2.naturalcycles.com --thresholdUnhealthy 5"
|
|
105
95
|
}
|
package/src/admin/login.html
CHANGED
|
@@ -6,38 +6,59 @@
|
|
|
6
6
|
<meta charset="utf-8" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
<style>
|
|
10
|
+
body {
|
|
11
|
+
max-width: 45rem;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: 40px 1rem;
|
|
14
|
+
font-family: system-ui, sans-serif;
|
|
15
|
+
font-size: 1.15rem;
|
|
16
|
+
line-height: 1.5;
|
|
17
|
+
}
|
|
18
|
+
pre {
|
|
19
|
+
padding: 1rem 1.4rem;
|
|
20
|
+
overflow-x: auto;
|
|
21
|
+
background: #f5f7ff;
|
|
22
|
+
border: 1px solid #ccc;
|
|
23
|
+
border-radius: 5px;
|
|
24
|
+
}
|
|
25
|
+
button {
|
|
26
|
+
padding: 1rem 1.4rem;
|
|
27
|
+
font-size: 1.5rem;
|
|
28
|
+
color: #fff;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
background: #0d47a1;
|
|
31
|
+
border: none;
|
|
32
|
+
border-radius: 5px;
|
|
33
|
+
}
|
|
34
|
+
button:hover {
|
|
35
|
+
opacity: 0.8;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
15
38
|
</head>
|
|
16
39
|
<body>
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
40
|
+
<pre id="loading">Checking login...</pre>
|
|
41
|
+
|
|
42
|
+
<div id="loggedIn" hidden>
|
|
43
|
+
<p>Logged in as:</p>
|
|
44
|
+
<pre><code id="email"></code></pre>
|
|
45
|
+
<pre><code id="token"></code></pre>
|
|
46
|
+
<button id="logoutButton">Logout</button>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div id="loggedOut" hidden>
|
|
50
|
+
<button id="loginBtn">Login</button>
|
|
30
51
|
</div>
|
|
31
52
|
|
|
32
53
|
<script type="module">
|
|
33
|
-
import {
|
|
34
|
-
import { initializeApp } from 'https://www.gstatic.com/firebasejs/9.1.2/firebase-app.js'
|
|
54
|
+
import { initializeApp } from 'https://www.gstatic.com/firebasejs/13.1.0/firebase-app.js'
|
|
35
55
|
import {
|
|
36
56
|
getAuth,
|
|
37
57
|
GoogleAuthProvider,
|
|
38
58
|
onAuthStateChanged,
|
|
39
59
|
signInWithRedirect,
|
|
40
|
-
|
|
60
|
+
signOut,
|
|
61
|
+
} from 'https://www.gstatic.com/firebasejs/13.1.0/firebase-auth.js'
|
|
41
62
|
|
|
42
63
|
const apiKey = '<%= firebaseApiKey %>'
|
|
43
64
|
const authDomain = '<%= firebaseAuthDomain %>'
|
|
@@ -47,84 +68,59 @@
|
|
|
47
68
|
alert(`Error: 'apiKey' or 'authDomain' is missing!`)
|
|
48
69
|
}
|
|
49
70
|
|
|
50
|
-
// Initialize Firebase
|
|
51
|
-
initializeApp({
|
|
52
|
-
apiKey,
|
|
53
|
-
authDomain,
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
const auth = getAuth()
|
|
57
|
-
const provider = new GoogleAuthProvider()
|
|
58
|
-
|
|
59
|
-
onAuthStateChanged(auth, user => {
|
|
60
|
-
// console.log('onAuthStateChanged, user: ', JSON.stringify(user, null, 2))
|
|
61
|
-
// console.log('onAuthStateChanged, user: ', user)
|
|
62
|
-
onUser(user)
|
|
63
|
-
})
|
|
64
|
-
|
|
65
71
|
const qs = Object.fromEntries(new URLSearchParams(location.search))
|
|
66
|
-
const { autoLogin,
|
|
72
|
+
const { autoLogin, adminTokenKey = 'admin_token' } = qs
|
|
67
73
|
const returnUrl = toSafeReturnUrl(qs.returnUrl)
|
|
68
74
|
// console.log(qs)
|
|
69
75
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
login: async function () {
|
|
79
|
-
try {
|
|
80
|
-
await signInWithRedirect(auth, provider)
|
|
81
|
-
} catch (err) {
|
|
82
|
-
logError(err)
|
|
83
|
-
}
|
|
84
|
-
},
|
|
76
|
+
// Initialize Firebase
|
|
77
|
+
const auth = getAuth(
|
|
78
|
+
initializeApp({
|
|
79
|
+
apiKey,
|
|
80
|
+
authDomain,
|
|
81
|
+
}),
|
|
82
|
+
)
|
|
83
|
+
const provider = new GoogleAuthProvider()
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
const el = {
|
|
86
|
+
loading: document.getElementById('loading'),
|
|
87
|
+
loggedIn: document.getElementById('loggedIn'),
|
|
88
|
+
loggedOut: document.getElementById('loggedOut'),
|
|
89
|
+
email: document.getElementById('email'),
|
|
90
|
+
token: document.getElementById('token'),
|
|
91
|
+
}
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
document.getElementById('loginBtn').addEventListener('click', login)
|
|
94
|
+
document.getElementById('logoutButton').addEventListener('click', logout)
|
|
91
95
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logError(err)
|
|
98
|
-
}
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
}).mount('#app')
|
|
96
|
+
onAuthStateChanged(auth, user => {
|
|
97
|
+
// console.log('onAuthStateChanged, user: ', JSON.stringify(user, null, 2))
|
|
98
|
+
// console.log('onAuthStateChanged, user: ', user)
|
|
99
|
+
void onUser(user)
|
|
100
|
+
})
|
|
102
101
|
|
|
103
|
-
if (logout)
|
|
102
|
+
if (qs.logout) void logout()
|
|
104
103
|
|
|
105
104
|
////
|
|
106
105
|
|
|
107
106
|
async function onUser(user) {
|
|
108
107
|
try {
|
|
109
108
|
// alert('onUser')
|
|
110
|
-
|
|
111
|
-
app.loading = false
|
|
109
|
+
render(user)
|
|
112
110
|
if (!user) {
|
|
113
|
-
if (autoLogin)
|
|
111
|
+
if (autoLogin) void login()
|
|
114
112
|
} else {
|
|
115
|
-
const token = await
|
|
113
|
+
const token = await user.getIdToken()
|
|
116
114
|
|
|
117
115
|
// alert('idToken')
|
|
118
116
|
// console.log(idToken)
|
|
119
|
-
|
|
120
|
-
token,
|
|
121
|
-
})
|
|
117
|
+
render(user, token)
|
|
122
118
|
|
|
123
119
|
await postToken(token)
|
|
124
120
|
|
|
125
121
|
// Redirect if needed
|
|
126
122
|
if (returnUrl) {
|
|
127
|
-
// alert(`Logged in as ${
|
|
123
|
+
// alert(`Logged in as ${user.email}, redirecting back...`)
|
|
128
124
|
location.href = returnUrl
|
|
129
125
|
}
|
|
130
126
|
}
|
|
@@ -133,6 +129,37 @@
|
|
|
133
129
|
}
|
|
134
130
|
}
|
|
135
131
|
|
|
132
|
+
function render(user, token = '') {
|
|
133
|
+
el.loading.hidden = true
|
|
134
|
+
el.loggedIn.hidden = !user
|
|
135
|
+
el.loggedOut.hidden = !!user
|
|
136
|
+
el.email.textContent = user?.email || ''
|
|
137
|
+
el.token.textContent = token
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function login() {
|
|
141
|
+
try {
|
|
142
|
+
await signInWithRedirect(auth, provider)
|
|
143
|
+
} catch (err) {
|
|
144
|
+
logError(err)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function logout() {
|
|
149
|
+
try {
|
|
150
|
+
await signOut(auth)
|
|
151
|
+
|
|
152
|
+
await postToken('logout') // magic string
|
|
153
|
+
|
|
154
|
+
if (qs.logout && returnUrl) {
|
|
155
|
+
alert('Logged out, redirecting back...')
|
|
156
|
+
location.href = returnUrl
|
|
157
|
+
}
|
|
158
|
+
} catch (err) {
|
|
159
|
+
logError(err)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
function logError(err) {
|
|
137
164
|
console.error(err)
|
|
138
165
|
alert('Error\n ' + JSON.stringify(err, null, 2))
|
|
@@ -10,7 +10,6 @@ import { pDelay } from '@naturalcycles/js-lib/promise/pDelay.js'
|
|
|
10
10
|
import type { UnixTimestampMillis } from '@naturalcycles/js-lib/types'
|
|
11
11
|
import type { CliOptions } from '@naturalcycles/nodejs-lib/args'
|
|
12
12
|
import { dimGrey, red } from '@naturalcycles/nodejs-lib/colors'
|
|
13
|
-
import { exec2 } from '@naturalcycles/nodejs-lib/exec2'
|
|
14
13
|
import { coloredHttpCode } from '../server/request.log.util.js'
|
|
15
14
|
|
|
16
15
|
export interface DeployHealthCheckOptions {
|
|
@@ -19,11 +18,6 @@ export interface DeployHealthCheckOptions {
|
|
|
19
18
|
maxTries?: number
|
|
20
19
|
timeoutSec?: number
|
|
21
20
|
intervalSec?: number
|
|
22
|
-
logOnFailure?: boolean
|
|
23
|
-
logOnSuccess?: boolean
|
|
24
|
-
gaeProject?: string
|
|
25
|
-
gaeService?: string
|
|
26
|
-
gaeVersion?: string
|
|
27
21
|
authHeader?: string
|
|
28
22
|
}
|
|
29
23
|
|
|
@@ -48,25 +42,6 @@ export const deployHealthCheckCliOptions = {
|
|
|
48
42
|
type: 'number',
|
|
49
43
|
default: 1,
|
|
50
44
|
},
|
|
51
|
-
logOnFailure: {
|
|
52
|
-
type: 'boolean',
|
|
53
|
-
default: true,
|
|
54
|
-
desc: 'Show server logs on health check failure (requires gaeProject, gaeService, gaeVersion)',
|
|
55
|
-
},
|
|
56
|
-
logOnSuccess: {
|
|
57
|
-
type: 'boolean',
|
|
58
|
-
default: false,
|
|
59
|
-
desc: 'Show server logs on health check success (requires gaeProject, gaeService, gaeVersion)',
|
|
60
|
-
},
|
|
61
|
-
gaeProject: {
|
|
62
|
-
type: 'string',
|
|
63
|
-
},
|
|
64
|
-
gaeService: {
|
|
65
|
-
type: 'string',
|
|
66
|
-
},
|
|
67
|
-
gaeVersion: {
|
|
68
|
-
type: 'string',
|
|
69
|
-
},
|
|
70
45
|
authHeader: {
|
|
71
46
|
type: 'string',
|
|
72
47
|
},
|
|
@@ -81,6 +56,7 @@ const inspectOpt: InspectOptions = {
|
|
|
81
56
|
* Requires up to `healthyThreshold` consecutive OK responses to succeed.
|
|
82
57
|
* Fails on up to `unhealthyThreshold` consecutive FAIL responses.
|
|
83
58
|
* Fails after maxTries.
|
|
59
|
+
* Throws on failure.
|
|
84
60
|
*/
|
|
85
61
|
export async function deployHealthCheck(
|
|
86
62
|
url: string,
|
|
@@ -92,11 +68,6 @@ export async function deployHealthCheck(
|
|
|
92
68
|
maxTries = 30,
|
|
93
69
|
timeoutSec = 30,
|
|
94
70
|
intervalSec = 1,
|
|
95
|
-
logOnFailure,
|
|
96
|
-
logOnSuccess,
|
|
97
|
-
gaeProject,
|
|
98
|
-
gaeService,
|
|
99
|
-
gaeVersion,
|
|
100
71
|
authHeader,
|
|
101
72
|
} = opt
|
|
102
73
|
|
|
@@ -116,24 +87,7 @@ export async function deployHealthCheck(
|
|
|
116
87
|
|
|
117
88
|
if (failed) {
|
|
118
89
|
console.log(red(`Health check failed!`))
|
|
119
|
-
|
|
120
|
-
if (logOnFailure) {
|
|
121
|
-
try {
|
|
122
|
-
exec2.spawn(
|
|
123
|
-
`gcloud app logs read --project ${gaeProject} --service ${gaeService} --version ${gaeVersion}`,
|
|
124
|
-
)
|
|
125
|
-
} catch {}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
process.exit(1)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (logOnSuccess) {
|
|
132
|
-
try {
|
|
133
|
-
exec2.spawn(
|
|
134
|
-
`gcloud app logs read --project ${gaeProject} --service ${gaeService} --version ${gaeVersion}`,
|
|
135
|
-
)
|
|
136
|
-
} catch {}
|
|
90
|
+
throw new Error(`Health check failed: ${doneReason}`)
|
|
137
91
|
}
|
|
138
92
|
|
|
139
93
|
async function makeAttempt(): Promise<void> {
|
package/src/deploy/index.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
import type { BackendCfg } from './backend.cfg.util.js'
|
|
2
|
-
import { getBackendCfg } from './backend.cfg.util.js'
|
|
3
|
-
import type { DeployInfo } from './deploy.model.js'
|
|
4
|
-
import { createAppYaml, createDeployInfo } from './deploy.util.js'
|
|
5
|
-
import { deployGae } from './deployGae.js'
|
|
6
1
|
import type { DeployHealthCheckOptions } from './deployHealthCheck.js'
|
|
7
2
|
import { deployHealthCheck } from './deployHealthCheck.js'
|
|
8
|
-
import { deployPrepare } from './deployPrepare.js'
|
|
9
3
|
|
|
10
|
-
export type {
|
|
4
|
+
export type { DeployHealthCheckOptions }
|
|
11
5
|
|
|
12
|
-
export {
|
|
13
|
-
createAppYaml,
|
|
14
|
-
createDeployInfo,
|
|
15
|
-
deployGae,
|
|
16
|
-
deployHealthCheck,
|
|
17
|
-
deployPrepare,
|
|
18
|
-
getBackendCfg,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export * from './deployInfo.util.js'
|
|
6
|
+
export { deployHealthCheck }
|