Commit a02cf620 authored by nanahira's avatar nanahira

so far

parent f2ab8518
......@@ -20,12 +20,15 @@
"class-validator": "^0.13.1",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"mustache": "^4.2.0",
"p-queue": "6.6.2",
"pg": "^8.7.1",
"pg-native": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"swagger-ui-express": "^4.1.6",
"tar": "^6.1.8",
"typeorm": "^0.2.37"
},
"devDependencies": {
......@@ -36,8 +39,10 @@
"@types/jest": "^26.0.24",
"@types/lodash": "^4.14.172",
"@types/multer": "^1.4.7",
"@types/mustache": "^4.1.2",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@types/tar": "^4.0.5",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"eslint": "^7.30.0",
......@@ -3187,6 +3192,15 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true
},
"node_modules/@types/minipass": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.0.tgz",
"integrity": "sha512-b2yPKwCrB8x9SB65kcCistMoe3wrYnxxt5rJSZ1kprw0uOXvhuKi9kTQ746Y+Pbqoh+9C0N4zt0ztmTnG9yg7A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/multer": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
......@@ -3196,6 +3210,12 @@
"@types/express": "*"
}
},
"node_modules/@types/mustache": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.1.2.tgz",
"integrity": "sha512-c4OVMMcyodKQ9dpwBwh3ofK9P6U9ZktKU9S+p33UqwMNN1vlv2P0zJZUScTshnx7OEoIIRcCFNQ904sYxZz8kg==",
"dev": true
},
"node_modules/@types/node": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz",
......@@ -3261,6 +3281,16 @@
"@types/superagent": "*"
}
},
"node_modules/@types/tar": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.5.tgz",
"integrity": "sha512-cgwPhNEabHaZcYIy5xeMtux2EmYBitfqEceBUi2t5+ETy4dW6kswt6WX4+HqLeiiKOo42EXbGiDmVJ2x+vi37Q==",
"dev": true,
"dependencies": {
"@types/minipass": "*",
"@types/node": "*"
}
},
"node_modules/@types/validator": {
"version": "13.6.3",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz",
......@@ -4312,6 +4342,14 @@
"fsevents": "~2.3.2"
}
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
"engines": {
"node": ">=10"
}
},
"node_modules/chrome-trace-event": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
......@@ -5427,6 +5465,11 @@
"node": ">= 0.6"
}
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
......@@ -5895,6 +5938,17 @@
"node": ">=12"
}
},
"node_modules/fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"dependencies": {
"minipass": "^3.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
......@@ -8271,6 +8325,29 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"node_modules/minipass": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"dependencies": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
......@@ -8313,6 +8390,14 @@
"node": ">= 0.10.0"
}
},
"node_modules/mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
"bin": {
"mustache": "bin/mustache"
}
},
"node_modules/mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
......@@ -8577,6 +8662,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"engines": {
"node": ">=4"
}
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
......@@ -8604,6 +8697,32 @@
"node": ">=8"
}
},
"node_modules/p-queue": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
"dependencies": {
"eventemitter3": "^4.0.4",
"p-timeout": "^3.2.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"dependencies": {
"p-finally": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
......@@ -10037,6 +10156,33 @@
"node": ">=6"
}
},
"node_modules/tar": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz",
"integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==",
"dependencies": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^3.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"engines": {
"node": ">= 10"
}
},
"node_modules/tar/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/terminal-link": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
......@@ -11349,8 +11495,7 @@
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yaml": {
"version": "1.10.2",
......@@ -13984,6 +14129,15 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==",
"dev": true
},
"@types/minipass": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@types/minipass/-/minipass-3.1.0.tgz",
"integrity": "sha512-b2yPKwCrB8x9SB65kcCistMoe3wrYnxxt5rJSZ1kprw0uOXvhuKi9kTQ746Y+Pbqoh+9C0N4zt0ztmTnG9yg7A==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/multer": {
"version": "1.4.7",
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.7.tgz",
......@@ -13993,6 +14147,12 @@
"@types/express": "*"
}
},
"@types/mustache": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.1.2.tgz",
"integrity": "sha512-c4OVMMcyodKQ9dpwBwh3ofK9P6U9ZktKU9S+p33UqwMNN1vlv2P0zJZUScTshnx7OEoIIRcCFNQ904sYxZz8kg==",
"dev": true
},
"@types/node": {
"version": "16.6.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.1.tgz",
......@@ -14058,6 +14218,16 @@
"@types/superagent": "*"
}
},
"@types/tar": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tar/-/tar-4.0.5.tgz",
"integrity": "sha512-cgwPhNEabHaZcYIy5xeMtux2EmYBitfqEceBUi2t5+ETy4dW6kswt6WX4+HqLeiiKOo42EXbGiDmVJ2x+vi37Q==",
"dev": true,
"requires": {
"@types/minipass": "*",
"@types/node": "*"
}
},
"@types/validator": {
"version": "13.6.3",
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz",
......@@ -14848,6 +15018,11 @@
"readdirp": "~3.6.0"
}
},
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
},
"chrome-trace-event": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
......@@ -15707,6 +15882,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
......@@ -16079,6 +16259,14 @@
"universalify": "^2.0.0"
}
},
"fs-minipass": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
"integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
"requires": {
"minipass": "^3.0.0"
}
},
"fs-monkey": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
......@@ -17883,6 +18071,23 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
},
"minipass": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
"integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
"requires": {
"yallist": "^4.0.0"
}
},
"minizlib": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
"integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
"requires": {
"minipass": "^3.0.0",
"yallist": "^4.0.0"
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
......@@ -17916,6 +18121,11 @@
"xtend": "^4.0.0"
}
},
"mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="
},
"mute-stream": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
......@@ -18119,6 +18329,11 @@
"integrity": "sha512-ycIL2+1V32th+8scbpTvyHNaHe02z0sjgh91XXjAk+ZeXoPN4Z46DVUnzdso0aX4KckKw0FNNFHdjZ2UsZvxiA==",
"dev": true
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
......@@ -18137,6 +18352,23 @@
"p-limit": "^2.2.0"
}
},
"p-queue": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
"integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
"requires": {
"eventemitter3": "^4.0.4",
"p-timeout": "^3.2.0"
}
},
"p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"requires": {
"p-finally": "^1.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
......@@ -19218,6 +19450,26 @@
"integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
"dev": true
},
"tar": {
"version": "6.1.8",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.8.tgz",
"integrity": "sha512-sb9b0cp855NbkMJcskdSYA7b11Q8JsX4qe4pyUAfHp+Y6jBjJeek2ZVlwEfWayshEIwlIzXx0Fain3QG9JPm2A==",
"requires": {
"chownr": "^2.0.0",
"fs-minipass": "^2.0.0",
"minipass": "^3.0.0",
"minizlib": "^2.1.1",
"mkdirp": "^1.0.3",
"yallist": "^4.0.0"
},
"dependencies": {
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
}
}
},
"terminal-link": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
......@@ -20115,8 +20367,7 @@
"yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"yaml": {
"version": "1.10.2",
......
......@@ -33,12 +33,15 @@
"class-validator": "^0.13.1",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"mustache": "^4.2.0",
"p-queue": "6.6.2",
"pg": "^8.7.1",
"pg-native": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0",
"swagger-ui-express": "^4.1.6",
"tar": "^6.1.8",
"typeorm": "^0.2.37"
},
"devDependencies": {
......@@ -49,8 +52,10 @@
"@types/jest": "^26.0.24",
"@types/lodash": "^4.14.172",
"@types/multer": "^1.4.7",
"@types/mustache": "^4.1.2",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@types/tar": "^4.0.5",
"@typescript-eslint/eslint-plugin": "^4.28.2",
"@typescript-eslint/parser": "^4.28.2",
"eslint": "^7.30.0",
......
......@@ -25,6 +25,7 @@ import { AppsJson } from '../utility/apps-json-type';
import { AppService } from '../app.service';
import { BlankReturnMessageDto } from '../dto/ReturnMessage.dto';
import { AssignAppDto } from '../dto/AssignApp.dto';
import { AppPrefixDto } from '../dto/AppPrefix.dto';
@Controller('api/admin')
@ApiTags('admin')
......@@ -56,14 +57,14 @@ export class AdminController {
}
@Delete('app/:id')
@ApiOperation({ summary: '创建 app' })
@ApiOperation({ summary: '删除 app' })
@ApiOkResponse({ type: BlankReturnMessageDto })
async deleteApp(@Param('id') id: string) {
return this.appService.deleteApp(id);
}
@Post('app/:id/assign')
@ApiOperation({ summary: '创建 app' })
@ApiOperation({ summary: '设置 app 管理者' })
@ApiOkResponse({ type: BlankReturnMessageDto })
async assignApp(
@Param('id') id: string,
......@@ -71,4 +72,14 @@ export class AdminController {
) {
return this.appService.assignApp(id, assignAppData.author);
}
@Post('app/:id/prefix')
@ApiOperation({ summary: '设置 app 打包前缀' })
@ApiOkResponse({ type: BlankReturnMessageDto })
async setAppPrefix(
@Param('id') id: string,
@Body(new ValidationPipe({ transform: true })) appPrefix: AppPrefixDto,
) {
return this.appService.setAppPrefix(id, appPrefix._prefix);
}
}
......@@ -7,6 +7,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
import { App } from './entities/App.entity';
import { AppHistory } from './entities/AppHistory.entity';
import { S3Service } from './s3/s3.service';
import { PackagerService } from './packager/packager.service';
const configModule = ConfigModule.forRoot();
......@@ -32,6 +33,6 @@ const configModule = ConfigModule.forRoot();
}),
],
controllers: [AppController, AdminController],
providers: [AppService, S3Service],
providers: [AppService, S3Service, PackagerService],
})
export class AppModule {}
......@@ -125,6 +125,14 @@ export class AppService extends ConsoleLogger {
);
}
async setAppPrefix(id: string, prefix: string) {
return this.updateResult(
() =>
this.db.getRepository(App).update({ id }, { packagePrefix: prefix }),
201,
);
}
async updateApp(user: MyCardUser, id: string, appData: AppsJson.App) {
if (!user) {
throw new BlankReturnMessageDto(401, 'Needs login').toException();
......
import { ApiProperty } from '@nestjs/swagger';
export class AppPrefixDto {
@ApiProperty({ description: '打包起始路径' })
prefix: string;
get _prefix() {
return this.prefix && this.prefix.length ? this.prefix : null;
}
}
......@@ -4,6 +4,7 @@ import { MyCardUser } from '../utility/mycard-auth';
import { AppBase } from './AppBase.entity';
import { AppHistory } from './AppHistory.entity';
import moment from 'moment';
import { join } from 'path';
@Entity()
export class App extends AppBase {
......@@ -17,6 +18,17 @@ export class App extends AppBase {
@Column({ nullable: true, select: false })
isDeleted: boolean;
@Column('varchar', { length: 128, nullable: true })
packagePrefix: string;
get packageFullPath() {
if (this.packagePrefix && this.packagePrefix.length) {
return join(this.id, this.packagePrefix);
} else {
return this.id;
}
}
isUserCanEditApp(u: MyCardUser) {
return u.admin || (this.author && this.author.includes(u.id));
}
......
import { MyCardAppMaintainerGuard } from './my-card-app-maintainer.guard';
describe('MyCardAppMaintainerGuard', () => {
it('should be defined', () => {
expect(new MyCardAppMaintainerGuard()).toBeDefined();
});
});
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { AppService } from './app.service';
import { getUserFromContext } from './utility/mycard-auth';
import { BlankReturnMessageDto } from './dto/ReturnMessage.dto';
@Injectable()
export class MyCardAppMaintainerGuard implements CanActivate {
constructor(private readonly appService: AppService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const user = await getUserFromContext(context);
if (!user) {
throw new BlankReturnMessageDto(401, 'Invalid user').toException();
}
if (!(await this.appService.isUserCanMaintainApp(user))) {
throw new BlankReturnMessageDto(403, 'Permission denied').toException();
}
return true;
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { PackagerService } from './packager.service';
describe('PackagerService', () => {
let service: PackagerService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PackagerService],
}).compile();
service = module.get<PackagerService>(PackagerService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { ConsoleLogger, Injectable } from '@nestjs/common';
import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import * as child_process from 'child_process';
import * as Mustache from 'mustache';
import tar from 'tar';
import { App } from '../entities/App.entity';
import PQueue from 'p-queue';
// eslint-disable-next-line @typescript-eslint/no-empty-function
function nothing() {}
@Injectable()
export class PackagerService extends ConsoleLogger {
workingPath: string;
releasePath: string;
downloadBaseUrl: string;
queueIdMap = new Map<string, PQueue>();
getQueue(id: string) {
if (!this.queueIdMap.has(id)) {
this.queueIdMap.set(id, new PQueue({ concurrency: 1 }));
}
return this.queueIdMap.get(id);
}
constructor() {
super('packager');
}
private async readdirRecursive(_path: string): Promise<string[]> {
const files = await fs.promises.readdir(_path, { encoding: 'utf-8' });
let result = files;
for (const file of files) {
const child = path.join(_path, file);
const stat = await fs.promises.stat(child);
if (stat.isDirectory()) {
result = result.concat(
(await this.readdirRecursive(child)).map((_file) =>
path.join(file, _file),
),
);
}
}
return result;
}
private caculateSHA256(file: string): Promise<string> {
return new Promise((resolve, reject) => {
const input = fs.createReadStream(file);
const hash = crypto.createHash('sha256');
hash.on('error', (error: Error) => {
reject(error);
});
input.on('error', (error: Error) => {
reject(error);
});
hash.on('readable', () => {
const data = hash.read();
if (data) {
resolve((<Buffer>data).toString('hex'));
}
});
input.pipe(hash);
});
}
private spawnAsync(
command: string,
args: string[],
options: child_process.SpawnOptions,
) {
return new Promise<void>((resolve, reject) => {
const child = child_process.spawn(command, args, options);
child.on('exit', (code) => {
if (code == 0) {
resolve();
} else {
reject(code);
}
});
child.on('error', (error) => {
reject(error);
});
});
}
private archive(
archive: string,
files: string[],
directory: string,
): Promise<void> {
return tar.c({ gzip: true, C: directory, file: archive }, files);
}
private async createDirectoryIfNotExists(directory: string) {
try {
await fs.promises.access(directory);
} catch (e) {
await fs.promises.mkdir(directory, { recursive: true });
}
}
private async unarchiveStream(stream: fs.ReadStream, directory: string) {
await this.createDirectoryIfNotExists(directory);
const unarchiveProcessStream = stream.pipe(tar.x({ C: directory }));
return new Promise<void>((resolve, reject) => {
unarchiveProcessStream.on('finish', resolve);
unarchiveProcessStream.on('error', reject);
});
}
private async packageProcess(app: App, stream: fs.ReadStream) {
const unarchiveDestination = path.join(
this.workingPath,
app.packageFullPath,
);
const packageWorkingRoot = path.join(this.workingPath, app.id);
}
async package(app: App, stream: fs.ReadStream) {
const queue = this.getQueue();
}
}
import { Test, TestingModule } from '@nestjs/testing';
import { S3Service } from './s3.service';
describe('S3Service', () => {
let service: S3Service;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [S3Service],
}).compile();
service = module.get<S3Service>(S3Service);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import {
ListObjectsCommand,
PutObjectCommand,
S3Client,
} from '@aws-sdk/client-s3';
import { createHash } from 'crypto';
@Injectable()
export class S3Service extends ConsoleLogger {
bucket: string;
prefix: string;
cdnUrl: string;
s3: S3Client;
constructor(config: ConfigService) {
super('s3');
this.bucket = config.get('S3_BUCKET');
this.prefix = config.get('S3_PREFIX');
this.cdnUrl =
config.get('S3_CDN_URL') ||
`${config.get('S3_ENDPOINT')}/${this.bucket}/${this.prefix}`;
this.s3 = new S3Client({
credentials: {
accessKeyId: config.get('S3_KEY'),
secretAccessKey: config.get('S3_SECRET'),
},
region: config.get('S3_REGION') || 'us-west-1',
endpoint: config.get('S3_ENDPOINT'),
forcePathStyle: true,
});
}
private async listObjects(path: string) {
const command = new ListObjectsCommand({
Bucket: this.bucket,
Prefix: path,
});
return this.s3.send(command);
}
async uploadAssets(file: Express.Multer.File) {
const fileSuffix = file.originalname.split('.').pop();
const hash = createHash('sha512').update(file.buffer).digest('hex');
const filename = `${hash}.${fileSuffix}`;
const path = `${this.prefix}/${filename}`;
const downloadUrl = `${this.cdnUrl}/${filename}`;
const checkExisting = await this.listObjects(path);
try {
if (
checkExisting.Contents &&
checkExisting.Contents.some((obj) => obj.Key === path)
) {
// already uploaded
} else {
await this.uploadFile(path, file.buffer, file.mimetype);
}
return downloadUrl;
} catch (e) {
this.error(`Failed to assign upload of file ${path}: ${e.toString()}`);
return null;
}
}
async uploadFile(path: string, buffer: Buffer, mime?: string) {
return this.s3.send(
new PutObjectCommand({
Bucket: this.bucket,
Key: path,
Body: buffer,
ContentType: mime,
}),
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment