js-dos 7.2.3 → 7.2.7
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/.github/workflows/codeql-analysis.yml +70 -0
- package/README.md +6 -5
- package/dist/emulators-ui-loader.png +0 -0
- package/dist/js-dos.css +208 -15
- package/dist/js-dos.js +2 -2
- package/dist/js-dos.js.map +1 -1
- package/dist/types/src/backend/jj/jj-config.d.ts +6 -0
- package/dist/types/src/backend/jj/latency.d.ts +8 -0
- package/dist/types/src/{personal.d.ts → backend/v7/personal.d.ts} +0 -0
- package/dist/types/src/backend/v7/v7-config.d.ts +8 -0
- package/dist/types/src/components/region.d.ts +6 -0
- package/dist/types/src/components/sidebar/active-token.d.ts +2 -0
- package/dist/types/src/components/sidebar/create-token.d.ts +2 -0
- package/dist/types/src/components/sidebar/latency-info.d.ts +7 -0
- package/dist/types/src/components/sidebar/main.d.ts +2 -0
- package/dist/types/src/components/sidebar/networking.d.ts +2 -0
- package/dist/types/src/components/tip.d.ts +2 -0
- package/dist/types/src/hardware-transport-layer.d.ts +4 -2
- package/dist/types/src/icons.d.ts +18 -0
- package/dist/types/src/player-app.d.ts +18 -0
- package/dist/types/src/player.d.ts +2 -1
- package/dist/types/src/xhr.d.ts +1 -0
- package/dist/types/{sls → v7-services}/src/personal.d.ts +0 -0
- package/dist/wdosbox.js +1 -1
- package/dist/wdosbox.js.symbols +3132 -3071
- package/dist/wdosbox.wasm +0 -0
- package/examples/digger/index.html +9 -7
- package/package.json +7 -4
- package/v7-services/.env +6 -0
- package/v7-services/ok.yaml +24 -0
- package/v7-services/package.json +17 -0
- package/v7-services/redeploy-ok.sh +25 -0
- package/v7-services/serverless.yml +140 -0
- package/v7-services/src/networking/day.ts +5 -0
- package/v7-services/src/networking/get-free-token.ts +33 -0
- package/v7-services/src/networking/get-token-cost.ts +31 -0
- package/v7-services/src/networking/get-token-info.ts +16 -0
- package/v7-services/src/networking/ipx/put-ipx-ip.ts +20 -0
- package/v7-services/src/networking/ipx/start-ipx.ts +16 -0
- package/v7-services/src/networking/ipx/stop-ipx.ts +19 -0
- package/v7-services/src/networking/limits.ts +3 -0
- package/v7-services/src/networking/token.ts +238 -0
- package/v7-services/src/ok.ts +7 -0
- package/v7-services/src/personal-acl.ts +33 -0
- package/v7-services/src/personal-put.ts +35 -0
- package/v7-services/src/personal.ts +18 -0
- package/v7-services/src/responses.ts +28 -0
- package/v7-services/src/user.ts +3 -0
- package/v7-services/yarn.lock +4400 -0
- package/dist/emulators-ui-loader.gif +0 -0
package/dist/wdosbox.wasm
CHANGED
|
Binary file
|
|
@@ -22,11 +22,13 @@
|
|
|
22
22
|
<div id="jsdos" />
|
|
23
23
|
<script>
|
|
24
24
|
let bundleUrl = "https://cdn.dos.zone/original/2X/2/24b00b14f118580763440ecaddcc948f8cb94f14.jsdos"; // digger
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// bundleUrl = "https://cdn.dos.zone/original/2X/6/6a2bfa87c031c2a11ab212758a5d914f7c112eeb.jsdos"; // dagnerous dave 2
|
|
26
|
+
// bundleUrl = "https://cdn.dos.zone/original/2X/1/1b063b2520052ebb504184667ac95e72423331de.jsdos"; // lost vikings
|
|
27
|
+
// bundleUrl = "https://cdn.dos.zone/custom/dos/doom.jsdos"; // doom
|
|
28
|
+
// bundleUrl = "https://cdn.dos.zone/original/2X/a/ac888d1660aa253f0ed53bd6c962c894125aaa19.jsdos"; // wolf 3d
|
|
29
|
+
// bundleUrl = "https://cdn.dos.zone/custom/dos/homm_2.jsdos"; // heroes of might and magic 2
|
|
30
|
+
// bundleUrl = "https://doszone-uploads.s3.dualstack.eu-central-1.amazonaws.com/original/2X/6/60b165c86771eadf24cb2f81aef4656b85d167a6.jsdos"; // transport tycoon deluxe
|
|
31
|
+
// bundleUrl = "doom_coop.jsdos";
|
|
30
32
|
let clientId = null;
|
|
31
33
|
emulators.pathPrefix = "/dist/";
|
|
32
34
|
Dos(document.getElementById("jsdos"), {
|
|
@@ -40,11 +42,11 @@
|
|
|
40
42
|
namespace: "test",
|
|
41
43
|
id: "aleksandr.guryanov@github.com",
|
|
42
44
|
};
|
|
43
|
-
|
|
45
|
+
|
|
44
46
|
return Promise.resolve(clientId);
|
|
45
47
|
}
|
|
46
48
|
}).run(bundleUrl);
|
|
47
49
|
</script>
|
|
48
50
|
</body>
|
|
49
51
|
|
|
50
|
-
</html>
|
|
52
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "js-dos",
|
|
3
|
-
"version": "7.2.
|
|
3
|
+
"version": "7.2.7",
|
|
4
4
|
"description": "Easiest API to run dos programs in browser",
|
|
5
5
|
"main": "dist/js-dos.js",
|
|
6
6
|
"types": "dist/types/src/player.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dosbox"
|
|
22
22
|
],
|
|
23
23
|
"author": "Alexander Guryanov (aka caiiiycuk)",
|
|
24
|
-
"license": "
|
|
24
|
+
"license": "GPL-2.0",
|
|
25
25
|
"bugs": {
|
|
26
26
|
"url": "https://github.com/caiiiycuk/js-dos/issues"
|
|
27
27
|
},
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
"browserify": "^17.0.0",
|
|
49
49
|
"core-js": "^3.17.1",
|
|
50
50
|
"del": "^5.1.0",
|
|
51
|
-
"emulators": "^0.72.
|
|
52
|
-
"emulators-ui": "^0.72.
|
|
51
|
+
"emulators": "^0.72.1",
|
|
52
|
+
"emulators-ui": "^0.72.9",
|
|
53
53
|
"eslint": "^7.32.0",
|
|
54
54
|
"eslint-config-google": "^0.14.0",
|
|
55
55
|
"gulp": "^4.0.2",
|
|
@@ -66,5 +66,8 @@
|
|
|
66
66
|
"typescript": "^4.4.4",
|
|
67
67
|
"vinyl-buffer": "^1.0.1",
|
|
68
68
|
"vinyl-source-stream": "^2.0.0"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"nanoid": "^3.1.30"
|
|
69
72
|
}
|
|
70
73
|
}
|
package/v7-services/.env
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
service: jsdos-ok
|
|
2
|
+
|
|
3
|
+
package:
|
|
4
|
+
exclude:
|
|
5
|
+
- 'node_modules/**'
|
|
6
|
+
|
|
7
|
+
provider:
|
|
8
|
+
name: aws
|
|
9
|
+
runtime: nodejs14.x
|
|
10
|
+
memorySize: 128
|
|
11
|
+
versionFunctions: false
|
|
12
|
+
|
|
13
|
+
functions:
|
|
14
|
+
ok:
|
|
15
|
+
handler: src/ok.ok
|
|
16
|
+
name: jsdos-ok
|
|
17
|
+
events:
|
|
18
|
+
- http:
|
|
19
|
+
path: /ok
|
|
20
|
+
method: get
|
|
21
|
+
cors: true
|
|
22
|
+
|
|
23
|
+
plugins:
|
|
24
|
+
- serverless-plugin-typescript
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "js-dos-v7-services",
|
|
3
|
+
"description": "",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"license": "GPL-2.0",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"nanoid": "^3.1.30"
|
|
8
|
+
},
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"@types/aws-lambda": "^8.10.88",
|
|
11
|
+
"@types/node": "^14.6.4",
|
|
12
|
+
"aws-sdk": "^2.1048.0",
|
|
13
|
+
"serverless": "^2.69.1",
|
|
14
|
+
"serverless-plugin-typescript": "^2.1.0",
|
|
15
|
+
"typescript": "^4.5.4"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
|
|
3
|
+
set -ex
|
|
4
|
+
|
|
5
|
+
yarn run sls deploy -c ok.yaml --verbose --region us-east-1
|
|
6
|
+
yarn run sls deploy -c ok.yaml --verbose --region us-east-2
|
|
7
|
+
yarn run sls deploy -c ok.yaml --verbose --region us-west-1
|
|
8
|
+
yarn run sls deploy -c ok.yaml --verbose --region us-west-2
|
|
9
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-central-1
|
|
10
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-west-1
|
|
11
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-west-2
|
|
12
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-south-1
|
|
13
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-west-3
|
|
14
|
+
yarn run sls deploy -c ok.yaml --verbose --region eu-north-1
|
|
15
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-east-1
|
|
16
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-south-1
|
|
17
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-northeast-3
|
|
18
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-northeast-2
|
|
19
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-southeast-1
|
|
20
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-southeast-2
|
|
21
|
+
yarn run sls deploy -c ok.yaml --verbose --region ap-northeast-1
|
|
22
|
+
yarn run sls deploy -c ok.yaml --verbose --region ca-central-1
|
|
23
|
+
yarn run sls deploy -c ok.yaml --verbose --region me-south-1
|
|
24
|
+
yarn run sls deploy -c ok.yaml --verbose --region sa-east-1
|
|
25
|
+
yarn run sls deploy -c ok.yaml --verbose --region af-south-1
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
org: caiiiycuk
|
|
2
|
+
app: jsdos-backend
|
|
3
|
+
service: jsdos-v7
|
|
4
|
+
|
|
5
|
+
provider:
|
|
6
|
+
name: aws
|
|
7
|
+
runtime: nodejs14.x
|
|
8
|
+
region: eu-central-1
|
|
9
|
+
memorySize: 256
|
|
10
|
+
versionFunctions: false
|
|
11
|
+
environment:
|
|
12
|
+
NETWORKING_TABLE: "v2_r1_dznetworking"
|
|
13
|
+
PUT_CURL: "jjs-putCurl"
|
|
14
|
+
RUN_INSTANCE: "jjs-runInstanceV2"
|
|
15
|
+
iamRoleStatements:
|
|
16
|
+
- Effect: Allow
|
|
17
|
+
Action:
|
|
18
|
+
- s3:ListBucket
|
|
19
|
+
Resource:
|
|
20
|
+
- "arn:aws:s3:::doszone-uploads"
|
|
21
|
+
- Effect: Allow
|
|
22
|
+
Action:
|
|
23
|
+
- s3:Get*
|
|
24
|
+
- s3:Put*
|
|
25
|
+
Resource:
|
|
26
|
+
- "arn:aws:s3:::doszone-uploads/*"
|
|
27
|
+
- Effect: Allow
|
|
28
|
+
Action:
|
|
29
|
+
- lambda:InvokeFunction
|
|
30
|
+
Resource:
|
|
31
|
+
- "arn:aws:lambda:${opt:region, self:provider.region}:*:function:${self:provider.environment.PUT_CURL}"
|
|
32
|
+
- "arn:aws:lambda:${opt:region, self:provider.region}:*:function:${self:provider.environment.RUN_INSTANCE}"
|
|
33
|
+
- Effect: Allow
|
|
34
|
+
Action:
|
|
35
|
+
- dynamodb:Query
|
|
36
|
+
- dynamodb:Scan
|
|
37
|
+
- dynamodb:GetItem
|
|
38
|
+
- dynamodb:PutItem
|
|
39
|
+
- dynamodb:UpdateItem
|
|
40
|
+
- dynamodb:DeleteItem
|
|
41
|
+
Resource:
|
|
42
|
+
- "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.NETWORKING_TABLE}"
|
|
43
|
+
|
|
44
|
+
functions:
|
|
45
|
+
getTokenCost:
|
|
46
|
+
handler: src/networking/get-token-cost.getTokenCost
|
|
47
|
+
name: jsdos-getTokenCost
|
|
48
|
+
events:
|
|
49
|
+
- http:
|
|
50
|
+
path: /token/cost/get
|
|
51
|
+
method: get
|
|
52
|
+
cors: true
|
|
53
|
+
getFreeToken:
|
|
54
|
+
handler: src/networking/get-free-token.getFreeToken
|
|
55
|
+
name: jsdos-getFreeToken
|
|
56
|
+
events:
|
|
57
|
+
- http:
|
|
58
|
+
path: /token/free/get
|
|
59
|
+
method: get
|
|
60
|
+
cors: true
|
|
61
|
+
getTokenInfo:
|
|
62
|
+
handler: src/networking/get-token-info.getTokenInfo
|
|
63
|
+
name: jsdos-getTokenInfo
|
|
64
|
+
events:
|
|
65
|
+
- http:
|
|
66
|
+
path: /token/info/get
|
|
67
|
+
method: get
|
|
68
|
+
cors: true
|
|
69
|
+
startIpx:
|
|
70
|
+
handler: src/networking/ipx/start-ipx.startIpx
|
|
71
|
+
name: jsdos-startIpx
|
|
72
|
+
events:
|
|
73
|
+
- http:
|
|
74
|
+
path: /token/ipx/start
|
|
75
|
+
method: get
|
|
76
|
+
cors: true
|
|
77
|
+
putIpxIp:
|
|
78
|
+
handler: src/networking/ipx/put-ipx-ip.putIpxIp
|
|
79
|
+
name: jsdos-putIpxIp
|
|
80
|
+
events:
|
|
81
|
+
- http:
|
|
82
|
+
path: /token/ipx/ip/put
|
|
83
|
+
method: put
|
|
84
|
+
cors: true
|
|
85
|
+
stopIpx:
|
|
86
|
+
handler: src/networking/ipx/stop-ipx.stopIpx
|
|
87
|
+
name: jsdos-stopIpx
|
|
88
|
+
events:
|
|
89
|
+
- http:
|
|
90
|
+
path: /token/ipx/stop
|
|
91
|
+
method: post
|
|
92
|
+
cors: true
|
|
93
|
+
personalPut:
|
|
94
|
+
handler: src/personal-put.personalPut
|
|
95
|
+
name: jsdos-personalPut
|
|
96
|
+
events:
|
|
97
|
+
- http:
|
|
98
|
+
path: /personal/put
|
|
99
|
+
method: post
|
|
100
|
+
cors: true
|
|
101
|
+
personalAcl:
|
|
102
|
+
handler: src/personal-acl.personalAcl
|
|
103
|
+
name: jsdos-personalAcl
|
|
104
|
+
events:
|
|
105
|
+
- http:
|
|
106
|
+
path: /personal/acl
|
|
107
|
+
method: post
|
|
108
|
+
cors: true
|
|
109
|
+
|
|
110
|
+
resources:
|
|
111
|
+
Resources:
|
|
112
|
+
dznetworking:
|
|
113
|
+
Type: 'AWS::DynamoDB::Table'
|
|
114
|
+
DeletionPolicy: Retain
|
|
115
|
+
Properties:
|
|
116
|
+
AttributeDefinitions:
|
|
117
|
+
-
|
|
118
|
+
AttributeName: key
|
|
119
|
+
AttributeType: S
|
|
120
|
+
-
|
|
121
|
+
AttributeName: day
|
|
122
|
+
AttributeType: N
|
|
123
|
+
KeySchema:
|
|
124
|
+
-
|
|
125
|
+
AttributeName: key
|
|
126
|
+
KeyType: HASH
|
|
127
|
+
GlobalSecondaryIndexes:
|
|
128
|
+
-
|
|
129
|
+
IndexName: day
|
|
130
|
+
KeySchema:
|
|
131
|
+
-
|
|
132
|
+
AttributeName: day
|
|
133
|
+
KeyType: HASH
|
|
134
|
+
Projection:
|
|
135
|
+
ProjectionType: ALL
|
|
136
|
+
TableName: ${self:provider.environment.NETWORKING_TABLE}
|
|
137
|
+
BillingMode: PAY_PER_REQUEST
|
|
138
|
+
|
|
139
|
+
plugins:
|
|
140
|
+
- serverless-plugin-typescript
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { error, badRequest, success } from "../responses";
|
|
2
|
+
import { getDayOrigin } from "./day";
|
|
3
|
+
import { freeTierSoftLimit, freeTierHardLimit } from "./limits";
|
|
4
|
+
import { createFreeToken, getFreeTierHardCount, getFreeTierSoftCount } from "./token";
|
|
5
|
+
|
|
6
|
+
export const getFreeToken = async (event: any) => {
|
|
7
|
+
const namespace = event.queryStringParameters?.namespace;
|
|
8
|
+
const id = event.queryStringParameters?.id;
|
|
9
|
+
const region = event.queryStringParameters?.region;
|
|
10
|
+
const day = getDayOrigin();
|
|
11
|
+
|
|
12
|
+
if (!namespace || !id || !region) {
|
|
13
|
+
return badRequest();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const softCount = await getFreeTierSoftCount(namespace, id, day);
|
|
17
|
+
if (softCount >= freeTierSoftLimit) {
|
|
18
|
+
return error("free-soft-limit");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const hardCount = await getFreeTierHardCount(day);
|
|
22
|
+
if (hardCount >= freeTierHardLimit) {
|
|
23
|
+
return error("free-hard-limit");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
return success({
|
|
28
|
+
token: await createFreeToken(namespace, id, day, region),
|
|
29
|
+
});
|
|
30
|
+
} catch (e) {
|
|
31
|
+
return error(e.message);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { error, badRequest, success } from "../responses";
|
|
2
|
+
import { getDayOrigin } from "./day";
|
|
3
|
+
import { freeTierSoftLimit, freeTierSec, freeTierHardLimit } from "./limits";
|
|
4
|
+
import { getFreeTierHardCount, getFreeTierSoftCount } from "./token";
|
|
5
|
+
|
|
6
|
+
export const getTokenCost = async (event: any) => {
|
|
7
|
+
const namespace = event.queryStringParameters?.namespace;
|
|
8
|
+
const id = event.queryStringParameters?.id;
|
|
9
|
+
const durationSec = Number.parseInt(event.queryStringParameters?.durationSec);
|
|
10
|
+
const day = getDayOrigin();
|
|
11
|
+
|
|
12
|
+
if (!namespace || !id || !durationSec || isNaN(durationSec)) {
|
|
13
|
+
return badRequest();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (freeTierSec !== durationSec) {
|
|
17
|
+
return error("30-min-required");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (await getFreeTierSoftCount(namespace, id, day) >= freeTierSoftLimit) {
|
|
21
|
+
return error("free-soft-limit");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (await getFreeTierHardCount(day) >= freeTierHardLimit) {
|
|
25
|
+
return error("free-hard-limit");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return success({
|
|
29
|
+
cost: 0,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { badRequest, error, success } from "../responses";
|
|
2
|
+
import { getToken } from "./token";
|
|
3
|
+
|
|
4
|
+
export const getTokenInfo = async (event: any) => {
|
|
5
|
+
const token = event.queryStringParameters?.token;
|
|
6
|
+
|
|
7
|
+
if (!token) {
|
|
8
|
+
return badRequest();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
return success(await getToken(token));
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return error(e.message);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { error, badRequest, success } from "../../responses";
|
|
2
|
+
import { putIp } from "../token";
|
|
3
|
+
|
|
4
|
+
export const putIpxIp = async (event: any) => {
|
|
5
|
+
const token = event.queryStringParameters?.token;
|
|
6
|
+
const arn = event.queryStringParameters?.arn;
|
|
7
|
+
const ip = event.queryStringParameters.ip;
|
|
8
|
+
|
|
9
|
+
if (!token || !arn || !ip) {
|
|
10
|
+
return badRequest();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
await putIp(token, arn, ip, "ipx");
|
|
15
|
+
return success({});
|
|
16
|
+
} catch (e) {
|
|
17
|
+
console.error("Can't update ipx ip", e);
|
|
18
|
+
return error(e.message);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { error, badRequest, success } from "../../responses";
|
|
2
|
+
import { newTask } from "../token";
|
|
3
|
+
|
|
4
|
+
export const startIpx = async (event: any) => {
|
|
5
|
+
const token = event.queryStringParameters?.token;
|
|
6
|
+
if (!token) {
|
|
7
|
+
return badRequest();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
try {
|
|
11
|
+
return success({ arn: await newTask(token, "ipx") });
|
|
12
|
+
} catch (e) {
|
|
13
|
+
console.error("Can't start ipx-server", e);
|
|
14
|
+
return error(e.message);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { error, badRequest, success } from "../../responses";
|
|
2
|
+
import { stopTask } from "../token";
|
|
3
|
+
|
|
4
|
+
export const stopIpx = async (event: any) => {
|
|
5
|
+
const token = event.queryStringParameters?.token;
|
|
6
|
+
const arn = event.queryStringParameters?.arn;
|
|
7
|
+
|
|
8
|
+
if (!token || !arn) {
|
|
9
|
+
return badRequest();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
await stopTask(token, arn, "ipx");
|
|
14
|
+
return success({});
|
|
15
|
+
} catch (e) {
|
|
16
|
+
console.error("Can't update ipx ip", e);
|
|
17
|
+
return error(e.message);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as AWS from "aws-sdk";
|
|
2
|
+
import { freeTierHardLimit, freeTierSec, freeTierSoftLimit } from "./limits";
|
|
3
|
+
import { customAlphabet } from "nanoid/async";
|
|
4
|
+
|
|
5
|
+
const dynamoDb = new AWS.DynamoDB.DocumentClient();
|
|
6
|
+
const NETWORKING_TABLE = process.env.NETWORKING_TABLE as string;
|
|
7
|
+
|
|
8
|
+
const lambda = new AWS.Lambda();
|
|
9
|
+
const RunInstanceLambda = process.env.RUN_INSTANCE as string;
|
|
10
|
+
|
|
11
|
+
const nanoid = customAlphabet("0123456789abcdefghijklmnopqrstuvwxyz", 6);
|
|
12
|
+
|
|
13
|
+
interface Token {
|
|
14
|
+
day: number,
|
|
15
|
+
region: string,
|
|
16
|
+
endTime: number,
|
|
17
|
+
ttlSec: number,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type TaskType = "ipx";
|
|
21
|
+
|
|
22
|
+
export async function getFreeTierSoftCount(namespace: string, id: string, day: number): Promise<number> {
|
|
23
|
+
const freeTierSoftKey = getFreeTierSoftKey(namespace, id, day);
|
|
24
|
+
const response = await dynamoDb.get({
|
|
25
|
+
TableName: NETWORKING_TABLE,
|
|
26
|
+
Key: {
|
|
27
|
+
key: freeTierSoftKey,
|
|
28
|
+
},
|
|
29
|
+
}).promise();
|
|
30
|
+
|
|
31
|
+
return response.Item?.count || 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function getFreeTierHardCount(day: number): Promise<number> {
|
|
35
|
+
const freeTierHardKey = getFreeTierHardKey(day);
|
|
36
|
+
const response = await dynamoDb.get({
|
|
37
|
+
TableName: NETWORKING_TABLE,
|
|
38
|
+
Key: {
|
|
39
|
+
key: freeTierHardKey,
|
|
40
|
+
},
|
|
41
|
+
}).promise();
|
|
42
|
+
|
|
43
|
+
return response.Item?.count || 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function createFreeToken(namespace: string, id: string, day: number, region: string): Promise<string> {
|
|
47
|
+
const freeTierSoftKey = getFreeTierSoftKey(namespace, id, day);
|
|
48
|
+
const freeTierHardKey = getFreeTierHardKey(day);
|
|
49
|
+
|
|
50
|
+
const inc = async (key: string, inc: number) => {
|
|
51
|
+
const updateParams: AWS.DynamoDB.DocumentClient.UpdateItemInput = {
|
|
52
|
+
TableName: NETWORKING_TABLE,
|
|
53
|
+
Key: {
|
|
54
|
+
key,
|
|
55
|
+
},
|
|
56
|
+
UpdateExpression: "ADD #count :inc",
|
|
57
|
+
ExpressionAttributeNames: {
|
|
58
|
+
"#count": "count",
|
|
59
|
+
},
|
|
60
|
+
ExpressionAttributeValues: {
|
|
61
|
+
":inc": inc,
|
|
62
|
+
},
|
|
63
|
+
ReturnValues: "ALL_NEW",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const response = await dynamoDb.update(updateParams).promise();
|
|
67
|
+
if (typeof response.Attributes?.count !== "number") {
|
|
68
|
+
console.error("Can't get updated count value for key " + key, response);
|
|
69
|
+
throw new Error("Can't get updated count value for key " + key);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return response.Attributes?.count as number;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const [softCount, hardCount] = await Promise.all([inc(freeTierSoftKey, 1), inc(freeTierHardKey, 1)]);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
if (softCount > freeTierSoftLimit) {
|
|
79
|
+
throw new Error("free-soft-limit");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (hardCount > freeTierHardLimit) {
|
|
83
|
+
throw new Error("free-hard-limit");
|
|
84
|
+
}
|
|
85
|
+
} catch (e: any) {
|
|
86
|
+
// rollback
|
|
87
|
+
await Promise.all([inc(freeTierSoftKey, -1), inc(freeTierHardKey, -1)]);
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return createToken(namespace, id, day, region, freeTierSec);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function getToken(token: string): Promise<Token> {
|
|
95
|
+
const item = (await dynamoDb.get({
|
|
96
|
+
TableName: NETWORKING_TABLE,
|
|
97
|
+
Key: {
|
|
98
|
+
key: getTokenKey(token),
|
|
99
|
+
},
|
|
100
|
+
}).promise()).Item as Token | undefined;
|
|
101
|
+
|
|
102
|
+
if (item === undefined) {
|
|
103
|
+
throw new Error("not-found");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
item.ttlSec = Math.round((item.endTime - new Date().getTime()) / 1000);
|
|
107
|
+
|
|
108
|
+
return item;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function createToken(namespace: string, id: string, day: number,
|
|
112
|
+
region: string, durationSec: number): Promise<string> {
|
|
113
|
+
let token: string | null = null;
|
|
114
|
+
while (token === null) {
|
|
115
|
+
token = await nanoid();
|
|
116
|
+
|
|
117
|
+
const createParams: AWS.DynamoDB.DocumentClient.PutItemInput = {
|
|
118
|
+
TableName: NETWORKING_TABLE,
|
|
119
|
+
Item: {
|
|
120
|
+
"key": getTokenKey(token),
|
|
121
|
+
namespace,
|
|
122
|
+
id,
|
|
123
|
+
day,
|
|
124
|
+
region,
|
|
125
|
+
durationSec,
|
|
126
|
+
"createdAt": new Date().getTime(),
|
|
127
|
+
"endTime": new Date().getTime() + durationSec * 1000,
|
|
128
|
+
},
|
|
129
|
+
ConditionExpression: "attribute_not_exists(#key)",
|
|
130
|
+
ExpressionAttributeNames: {
|
|
131
|
+
"#key": "key",
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
await dynamoDb.put(createParams).promise();
|
|
137
|
+
} catch (e) {
|
|
138
|
+
console.error(e);
|
|
139
|
+
token = null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return token;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function newTask(tokenId: string, task: TaskType) {
|
|
147
|
+
const token = await getToken(tokenId);
|
|
148
|
+
|
|
149
|
+
if (token.ttlSec < 60) { // 1min to start container
|
|
150
|
+
throw new Error("ended");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = await lambda.invoke({
|
|
154
|
+
FunctionName: RunInstanceLambda,
|
|
155
|
+
Payload: JSON.stringify({
|
|
156
|
+
token: tokenId,
|
|
157
|
+
region: token.region,
|
|
158
|
+
task,
|
|
159
|
+
}),
|
|
160
|
+
}).promise();
|
|
161
|
+
|
|
162
|
+
if (!result.Payload) {
|
|
163
|
+
throw new Error("unable-to-start-task");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const payload = JSON.parse(JSON.parse(result.Payload as string).body);
|
|
167
|
+
|
|
168
|
+
if (payload.success === false) {
|
|
169
|
+
throw new Error(payload.errorCode);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const arn = payload.arn;
|
|
173
|
+
|
|
174
|
+
if (arn === undefined) {
|
|
175
|
+
throw new Error("ARN missed in runInstanceV2 response");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const updateParams: AWS.DynamoDB.DocumentClient.UpdateItemInput = {
|
|
179
|
+
TableName: NETWORKING_TABLE,
|
|
180
|
+
Key: {
|
|
181
|
+
key: getTokenKey(tokenId),
|
|
182
|
+
},
|
|
183
|
+
UpdateExpression: `SET ${task}Arn = :arn`,
|
|
184
|
+
ExpressionAttributeValues: {
|
|
185
|
+
":arn": arn,
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
await dynamoDb.update(updateParams).promise();
|
|
190
|
+
|
|
191
|
+
return arn;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function putIp(token: string, arn: string, ip: string, task: TaskType) {
|
|
195
|
+
const updateParams: AWS.DynamoDB.DocumentClient.UpdateItemInput = {
|
|
196
|
+
TableName: NETWORKING_TABLE,
|
|
197
|
+
Key: {
|
|
198
|
+
key: getTokenKey(token),
|
|
199
|
+
},
|
|
200
|
+
UpdateExpression: `SET ${task}Ip = :ip`,
|
|
201
|
+
ConditionExpression: `${task}Arn = :arn`,
|
|
202
|
+
ExpressionAttributeValues: {
|
|
203
|
+
":arn": arn,
|
|
204
|
+
":ip": ip,
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
await dynamoDb.update(updateParams).promise();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function stopTask(token: string, arn: string, task: TaskType) {
|
|
212
|
+
const updateParams: AWS.DynamoDB.DocumentClient.UpdateItemInput = {
|
|
213
|
+
TableName: NETWORKING_TABLE,
|
|
214
|
+
Key: {
|
|
215
|
+
key: getTokenKey(token),
|
|
216
|
+
},
|
|
217
|
+
UpdateExpression: `REMOVE ${task}Arn, ${task}Ip`,
|
|
218
|
+
ConditionExpression: `${task}Arn = :arn`,
|
|
219
|
+
ExpressionAttributeValues: {
|
|
220
|
+
":arn": arn,
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
await dynamoDb.update(updateParams).promise();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
function getFreeTierSoftKey(namespace: string, id: string, day: number) {
|
|
229
|
+
return "free-soft " + day + " " + id + "@" + namespace;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function getFreeTierHardKey(day: number) {
|
|
233
|
+
return "free-hard " + day;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function getTokenKey(token: string) {
|
|
237
|
+
return "token " + token;
|
|
238
|
+
}
|