diff --git a/.gitignore b/.gitignore index f09b18c..a34e4d9 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,6 @@ Thumbs.db # api /api/node_modules -node_modules/ \ No newline at end of file +node_modules/ +frontend/node_modules/ +frontend/dist/ \ No newline at end of file diff --git a/api/game_list/index.js b/api/game_list/index.js index 19300ae..545bd0a 100644 --- a/api/game_list/index.js +++ b/api/game_list/index.js @@ -12,6 +12,7 @@ module.exports = async function (context, req) { const body = req.body || {}; const page = body.page || 1; const size = body.size || 10; + const columns = body.columns || false; let query_conditions = {}; ["Name", "Developer", "Distributer"].forEach(key => { @@ -23,10 +24,17 @@ module.exports = async function (context, req) { context.log(query_conditions); const data = await collection.find(query_conditions).skip((page - 1) * size).limit(size).toArray(); const total = await collection.countDocuments(query_conditions); - context.res = { + let response = { status: 200, body: { code: 200, total: total, data: data, msg: "SUCCESS" } }; + + if (columns) { + const names = await collection.distinct('Name'); + response.body.names = names; + } + + context.res = response; } catch (e) { context.res = { status: 500, diff --git a/api/new_game/function.json b/api/new_game/function.json new file mode 100644 index 0000000..9f2d4d8 --- /dev/null +++ b/api/new_game/function.json @@ -0,0 +1,19 @@ +{ + "bindings": [ + { + "authLevel": "anonymous", + "type": "httpTrigger", + "direction": "in", + "name": "req", + "methods": [ + "get", + "post" + ] + }, + { + "type": "http", + "direction": "out", + "name": "res" + } + ] +} \ No newline at end of file diff --git a/api/new_game/index.js b/api/new_game/index.js new file mode 100644 index 0000000..8bc27fb --- /dev/null +++ b/api/new_game/index.js @@ -0,0 +1,64 @@ +const { MongoClient } = require('mongodb'); +const connectionString = "mongodb://wecloud:riQjoBDxr09fnyu8yCa9FeP19Op8UNB1ZFVkxEw3a1cQy25YrPnTzHx460TWBp8vaWIm5ciDRGh6ACDbZ5nHeg==@wecloud.mongo.cosmos.azure.com:10255/?ssl=true&retrywrites=false&replicaSet=globaldb&maxIdleTimeMS=120000&appName=@wecloud@" +const client = new MongoClient(connectionString); +const collectionName = "wegame"; +const dbName = "wegame"; +module.exports = async function (context, req) { + try { + await client.connect(); + const db = client.db(dbName); + const gameCollection = db.collection(collectionName); + + const gameData = req.body || {}; + const necessaryField = "Name"; + + if (!gameData[necessaryField]) { + context.res = { + status: 400, // Bad Request + body: { code: 400, msg: `Field: [${necessaryField}] is required` } + }; + return; + } + + const existingGame = await gameCollection.findOne({ "Name": gameData[necessaryField] }); + if (existingGame) { + context.res = { + status: 400, // Bad Request + body: { code: 400, msg: `A game with the same title [${gameData[necessaryField]}] already exists` } + }; + return; + } + + // Convert fields to float where possible + for (const key in gameData) { + if (gameData.hasOwnProperty(key)) { + try { + gameData[key] = parseFloat(gameData[key]); + } catch (e) { + // Keep the original value if it's not a number + } + } + } + + // Add timestamps + gameData.date_added = new Date(); + gameData.update_time = Date.now(); + + // Insert the new game data + await gameCollection.insertOne(gameData); + + context.res = { + status: 200, // OK + body: { code: 200, msg: "SUCCESS" } + }; + } catch (e) { + context.res = { + status: 500, // Internal Server Error + body: { code: 500, msg: `Error: ${e.message}` } + }; + } finally { + if (client) { + client.close(); + } + } +}; \ No newline at end of file diff --git a/api/new_game/sample.dat b/api/new_game/sample.dat new file mode 100644 index 0000000..26aac46 --- /dev/null +++ b/api/new_game/sample.dat @@ -0,0 +1,3 @@ +{ + "name": "Azure" +} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 8cbd9da..f0342b6 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -428,6 +428,105 @@ "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", "dev": true }, + "@azure/abort-controller": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz", + "integrity": "sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-auth": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@azure/core-auth/-/core-auth-1.5.0.tgz", + "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.1.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-http": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@azure/core-http/-/core-http-3.0.4.tgz", + "integrity": "sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-util": "^1.1.1", + "@azure/logger": "^1.0.0", + "@types/node-fetch": "^2.5.0", + "@types/tunnel": "^0.0.3", + "form-data": "^4.0.0", + "node-fetch": "^2.6.7", + "process": "^0.11.10", + "tslib": "^2.2.0", + "tunnel": "^0.0.6", + "uuid": "^8.3.0", + "xml2js": "^0.5.0" + } + }, + "@azure/core-lro": { + "version": "2.5.4", + "resolved": "https://registry.npmmirror.com/@azure/core-lro/-/core-lro-2.5.4.tgz", + "integrity": "sha512-3GJiMVH7/10bulzOKGrrLeG/uCBH/9VtxqaMcB9lIqAeamI/xYQSHJL/KcsLDuH+yTjYpro/u6D/MuRe4dN70Q==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-util": "^1.2.0", + "@azure/logger": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/core-paging": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@azure/core-paging/-/core-paging-1.5.0.tgz", + "integrity": "sha512-zqWdVIt+2Z+3wqxEOGzR5hXFZ8MGKK52x4vFLw8n58pR6ZfKRx3EXYTxTaYxYHc/PexPUTyimcTWFJbji9Z6Iw==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/core-tracing": { + "version": "1.0.0-preview.13", + "resolved": "https://registry.npmmirror.com/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", + "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "requires": { + "@opentelemetry/api": "^1.0.1", + "tslib": "^2.2.0" + } + }, + "@azure/core-util": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/@azure/core-util/-/core-util-1.6.1.tgz", + "integrity": "sha512-h5taHeySlsV9qxuK64KZxy4iln1BtMYlNt5jbuEFN3UFSAd1EwKg/Gjl5a6tZ/W8t6li3xPnutOx7zbDyXnPmQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "tslib": "^2.2.0" + } + }, + "@azure/logger": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/@azure/logger/-/logger-1.0.4.tgz", + "integrity": "sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@azure/storage-blob": { + "version": "12.17.0", + "resolved": "https://registry.npmmirror.com/@azure/storage-blob/-/storage-blob-12.17.0.tgz", + "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", + "requires": { + "@azure/abort-controller": "^1.0.0", + "@azure/core-http": "^3.0.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.1.1", + "@azure/core-tracing": "1.0.0-preview.13", + "@azure/logger": "^1.0.0", + "events": "^3.0.0", + "tslib": "^2.2.0" + } + }, "@babel/code-frame": { "version": "7.18.6", "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.18.6.tgz", @@ -1961,6 +2060,11 @@ "which": "^2.0.2" } }, + "@opentelemetry/api": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/@opentelemetry/api/-/api-1.7.0.tgz", + "integrity": "sha512-AdY5wvN0P2vXBi3b29hxZgSFvdhdxPB9+f0B6s//P9Q8nibRWeA3cHm8UmLpio9ABigkVHJ5NMPk+Mz8VCCyrw==" + }, "@schematics/angular": { "version": "14.2.9", "resolved": "https://registry.npmmirror.com/@schematics/angular/-/angular-14.2.9.tgz", @@ -2113,8 +2217,16 @@ "@types/node": { "version": "18.11.9", "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==", - "dev": true + "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + }, + "@types/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/@types/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", + "requires": { + "@types/node": "*", + "form-data": "^4.0.0" + } }, "@types/parse-json": { "version": "4.0.0", @@ -2168,6 +2280,14 @@ "@types/node": "*" } }, + "@types/tunnel": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/@types/tunnel/-/tunnel-0.0.3.tgz", + "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", + "requires": { + "@types/node": "*" + } + }, "@types/ws": { "version": "8.5.3", "resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.3.tgz", @@ -2560,6 +2680,11 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -3012,6 +3137,14 @@ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz", @@ -3480,6 +3613,11 @@ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz", @@ -3968,8 +4106,7 @@ "events": { "version": "3.3.0", "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" }, "execa": { "version": "5.1.1", @@ -4204,6 +4341,16 @@ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", @@ -5563,14 +5710,12 @@ "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" }, "mime-types": { "version": "2.1.35", "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, "requires": { "mime-db": "1.52.0" } @@ -5849,6 +5994,14 @@ "dev": true, "optional": true }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, "node-forge": { "version": "1.3.1", "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-1.3.1.tgz", @@ -6807,6 +6960,11 @@ "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", "dev": true }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -7258,8 +7416,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schema-utils": { "version": "2.7.1", @@ -7965,6 +8122,11 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmmirror.com/tree-kill/-/tree-kill-1.2.2.tgz", @@ -7976,6 +8138,11 @@ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.1.tgz", "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + }, "type-fest": { "version": "0.21.3", "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", @@ -8102,8 +8269,7 @@ "uuid": { "version": "8.3.2", "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" }, "validate-npm-package-license": { "version": "3.0.4", @@ -8164,6 +8330,11 @@ "defaults": "^1.0.3" } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, "webpack": { "version": "5.74.0", "resolved": "https://registry.npmmirror.com/webpack/-/webpack-5.74.0.tgz", @@ -8383,6 +8554,15 @@ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", "dev": true }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", @@ -8456,6 +8636,20 @@ "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index dedbe71..d5f137d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -18,6 +18,7 @@ "@angular/platform-browser": "^14.2.0", "@angular/platform-browser-dynamic": "^14.2.0", "@angular/router": "^14.2.0", + "@azure/storage-blob": "^12.17.0", "ng-zorro-antd": "^14.2.1", "rxjs": "~7.5.0", "tslib": "^2.3.0", diff --git a/frontend/src/app/app-routing.module.ts b/frontend/src/app/app-routing.module.ts index a91b9bb..462642c 100644 --- a/frontend/src/app/app-routing.module.ts +++ b/frontend/src/app/app-routing.module.ts @@ -17,6 +17,11 @@ const routes: Routes = [ loadChildren: () => import('./pages/layout/layout.module').then((m) => m.LayoutModule), }, + { + path:'myUpload', + loadChildren: () => + import('./pages/myUpload/myUpload.module').then((m) => m.MyUploadModule), + }, { path: '**', component: PageNotFoundComponent }, // Wildcard route for a 404 page ]; diff --git a/frontend/src/app/pages/FileUploadComponent/file-upload.component.html b/frontend/src/app/pages/FileUploadComponent/file-upload.component.html new file mode 100644 index 0000000..6de85bd --- /dev/null +++ b/frontend/src/app/pages/FileUploadComponent/file-upload.component.html @@ -0,0 +1,27 @@ +
+ +

+ +

+

Click or drag your videos or photos to this area to upload

+

+ Support for a single or bulk upload. Strictly prohibit from uploading company data or other band files +

+
+ + + +
+ + +
+ + +
diff --git a/frontend/src/app/pages/FileUploadComponent/file-upload.component.scss b/frontend/src/app/pages/FileUploadComponent/file-upload.component.scss new file mode 100644 index 0000000..f793902 --- /dev/null +++ b/frontend/src/app/pages/FileUploadComponent/file-upload.component.scss @@ -0,0 +1,23 @@ +::ng-deep .ant-list-vertical .ant-list-item-action > li { + color: #999; +} +.wegame-search { + width: 500px; + margin: 0 auto; + display: block; + margin-bottom: 30px; +} +a { + color: #99a1c8; +} +.card-shadow:hover { + box-shadow: 0 1px 2px -2px #00000029, 0 3px 6px #0000001f, + 0 5px 12px 4px #00000017; +} +.text-overflow { + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + text-overflow: ellipsis; +} diff --git a/frontend/src/app/pages/FileUploadComponent/file-upload.component.ts b/frontend/src/app/pages/FileUploadComponent/file-upload.component.ts new file mode 100644 index 0000000..a7b68f4 --- /dev/null +++ b/frontend/src/app/pages/FileUploadComponent/file-upload.component.ts @@ -0,0 +1,77 @@ +import { Component, OnInit } from '@angular/core'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzUploadChangeParam, NzUploadFile } from 'ng-zorro-antd/upload'; +import { BlobServiceClient, ContainerClient } from '@azure/storage-blob'; +import { NzModalService } from 'ng-zorro-antd/modal'; + +@Component({ + selector: 'app-file-upload', + templateUrl: './file-upload.component.html', + styleUrls: ['./file-upload.component.scss'] +}) +export class FileUploadComponent implements OnInit { + uploading = false; + fileList: NzUploadFile[] = []; + uploadTitle = ''; + sasUrl = 'https://wecloudblob.blob.core.windows.net/?sv=2022-11-02&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2023-12-18T18:13:02Z&st=2023-12-18T10:13:02Z&spr=https,http&sig=pMEPhuudBIztt%2FjWBY%2BWIeGqWHN6BdYMAzUmGSy7PUU%3D'; + containerName = 'games'; + blobServiceClient: BlobServiceClient; + containerClient: ContainerClient; + + constructor(private $message: NzMessageService, private modalService: NzModalService) { + this.blobServiceClient = new BlobServiceClient(this.sasUrl); + this.containerClient = this.blobServiceClient.getContainerClient(this.containerName); + } + + ngOnInit(): void { + // Additional initialization logic if required + } + + // Handle the file before uploading + beforeUpload = (file: NzUploadFile, _fileList: NzUploadFile[]) => { + this.fileList = this.fileList.concat(file); + return false; // Prevent automatic upload + }; + + handleUpload(): void { + this.modalService.confirm({ + nzTitle: 'Confirm Upload', + nzContent: `Title: ${this.uploadTitle}
Number of files: ${this.fileList.length}`, + nzOnOk: () => this.onConfirmUpload() + }); + } + + async onConfirmUpload() { + this.uploading = true; + this.uploadTitle = ''; // Reset the title for the next upload + + for (const file of this.fileList) { + const blob = new Blob([file as any], { type: file.type }); + await this.uploadFileToAzure(this.uploadTitle, file.name, blob); + } + + this.fileList = []; + this.uploading = false; + } + + handleChange({ file, fileList }: NzUploadChangeParam): void { + const status = file.status; + if (status === 'done') { + this.$message.success(`${file.name} file uploaded successfully.`); + } else if (status === 'error') { + this.$message.error(`${file.name} file upload failed.`); + } + this.fileList = fileList; + } + + async uploadFileToAzure(uploadTitle: string, fileName: string, blob: Blob) { + try { + const newFileName = `${uploadTitle}_${fileName}`; + const blockBlobClient = this.containerClient.getBlockBlobClient(newFileName); + await blockBlobClient.uploadData(blob); + this.$message.success(`${fileName} uploaded successfully.`); + } catch (error) { + this.$message.error(`Upload failed: ${error}`); + } + } +} diff --git a/frontend/src/app/pages/FileUploadComponent/file-upload.module.ts b/frontend/src/app/pages/FileUploadComponent/file-upload.module.ts new file mode 100644 index 0000000..715d14c --- /dev/null +++ b/frontend/src/app/pages/FileUploadComponent/file-upload.module.ts @@ -0,0 +1,19 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from '../../shared/shared.module'; +import { NzUploadModule } from 'ng-zorro-antd/upload'; +import { NzSpinModule } from 'ng-zorro-antd/spin'; +import { FileUploadComponent } from './file-upload.component'; + +@NgModule({ + imports: [ + CommonModule, + SharedModule, + NzUploadModule, + NzSpinModule, + ], + declarations: [FileUploadComponent], + exports: [FileUploadComponent], +}) +export class FileUploadModule {} diff --git a/frontend/src/app/pages/layout/layout-routing.module.ts b/frontend/src/app/pages/layout/layout-routing.module.ts index 85115c7..873fda2 100644 --- a/frontend/src/app/pages/layout/layout-routing.module.ts +++ b/frontend/src/app/pages/layout/layout-routing.module.ts @@ -20,6 +20,11 @@ const routes: Routes = [ (m) => m.MovieDetailsModule ), }, + { + path: 'myUpload', + loadChildren: () => + import('../myUpload/myUpload.module').then((m) => m.MyUploadModule), + } ], }, ]; diff --git a/frontend/src/app/pages/layout/layout.component.html b/frontend/src/app/pages/layout/layout.component.html index f252a9b..845f6e1 100644 --- a/frontend/src/app/pages/layout/layout.component.html +++ b/frontend/src/app/pages/layout/layout.component.html @@ -6,6 +6,9 @@
  • Game Toplist
  • +
  • + My Upload +
  • {{ username diff --git a/frontend/src/app/pages/list/list.component.html b/frontend/src/app/pages/list/list.component.html index 13ccd20..cfdf77f 100644 --- a/frontend/src/app/pages/list/list.component.html +++ b/frontend/src/app/pages/list/list.component.html @@ -39,7 +39,7 @@ (click)="isVisible = true" style="margin-left: 15px" > - Add + Add Medias @@ -60,7 +60,7 @@ text-decoration: underline; " (click)="toDetail(item)" - >{{ item.Name }}{{ item.Name }} {{ item.Developer }} @@ -118,24 +118,35 @@
    -
    + {{ field.label }} + >Name - + [nzPlaceHolder]="addField[0].value" + nzShowSearch + > + + + + +
    +
    diff --git a/frontend/src/app/pages/list/list.component.ts b/frontend/src/app/pages/list/list.component.ts index a051ef2..6fb9162 100644 --- a/frontend/src/app/pages/list/list.component.ts +++ b/frontend/src/app/pages/list/list.component.ts @@ -108,9 +108,12 @@ export class ListComponent implements OnInit { Distributer: [null], Developer: [null], }); - this.getWegameList(); + this.getWegameList(true); } + getNamesFromLocalStorage() { + return JSON.parse(localStorage.getItem('names') || '[]'); + } submitForm(): void { if (this.addForm.valid) { let params = { ...this.addForm.value }; @@ -152,16 +155,17 @@ export class ListComponent implements OnInit { return [total, positive]; } - getWegameList() { + getWegameList(columns:boolean = false) { let params = { page: this.page, size: this.size, + columns: columns, ...this.searchForm.value, }; this.apiService.post('game_list', params).subscribe( (res: any) => { this.loading = false; - const { code, data, total } = res; + const { code, data, total, names } = res; if (code == 200) { this.loading = false; this.wegameList = data; @@ -176,6 +180,9 @@ export class ListComponent implements OnInit { }); this.total = total; console.log('this.wegameList', this.wegameList); + if (names) { + localStorage.setItem('names', JSON.stringify(names)); + } } else { this.wegameList = []; } diff --git a/frontend/src/app/pages/list/list.module.ts b/frontend/src/app/pages/list/list.module.ts index ed57409..cee8576 100644 --- a/frontend/src/app/pages/list/list.module.ts +++ b/frontend/src/app/pages/list/list.module.ts @@ -3,11 +3,15 @@ import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; import { SharedModule } from '../../shared/shared.module'; import { ListComponent } from './list.component'; +import { FileUploadModule } from '../FileUploadComponent/file-upload.module'; +import { NzSelectModule } from 'ng-zorro-antd/select' @NgModule({ imports: [ CommonModule, SharedModule, + FileUploadModule, + NzSelectModule, RouterModule.forChild([ { path: '', diff --git a/frontend/src/app/pages/myUpload/myUpload.component.ts b/frontend/src/app/pages/myUpload/myUpload.component.ts new file mode 100644 index 0000000..c0de56e --- /dev/null +++ b/frontend/src/app/pages/myUpload/myUpload.component.ts @@ -0,0 +1,128 @@ +import { Component, OnInit } from '@angular/core'; +import { NzMessageService } from 'ng-zorro-antd/message'; +import { NavigateService } from 'src/app/services/navigate.service'; +import { ApiService } from 'src/app/services/api.service'; +import { BlobServiceClient, ContainerClient } from '@azure/storage-blob'; +import { HttpClient } from '@angular/common/http'; +import { lastValueFrom } from 'rxjs'; +import { NzModalService } from 'ng-zorro-antd/modal'; +import { HttpHeaders } from '@angular/common/http'; + +@Component({ + selector: 'app-favorite', + templateUrl: './myUpload.html', + styleUrls: ['./myUpload.scss'], +}) +export class MyUploadComponent implements OnInit { + favoriteList: any = []; + loading: boolean = true; + username: any; + images: { + url: any, + type: any, + uploadTime:any, + blobUrl:any , + picTitle:any + }[] = []; + // Azure Storage SAS URL + sasUrl = 'https://wecloudblob.blob.core.windows.net/?sv=2022-11-02&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2023-12-18T18:13:02Z&st=2023-12-18T10:13:02Z&spr=https,http&sig=pMEPhuudBIztt%2FjWBY%2BWIeGqWHN6BdYMAzUmGSy7PUU%3D'; + // sasToken = 'si=asset&sv=2022-11-02&sr=c&sig=OA8DpIpkl1ak4ZG%2BA7BzMtIRbI6carOTxLtzAcHa9pg%3D' + constructor( + private apiService: ApiService, + private $message: NzMessageService, + private navigateService: NavigateService, + private http: HttpClient, + private modalService: NzModalService + + ) {} + + ngOnInit() { + this.username = localStorage.getItem('username') as any; + this.loadImagesFromAzure(); + + } + + private extractSasToken(): string { + // Extract the SAS token from your sasUrl + const sasToken = this.sasUrl.split('?')[1]; + return sasToken ? `?${sasToken}` : ''; + } + + previewImage(image: { url: string, type: string }) { + let content = ''; + if (image.type.startsWith('image')) { + content = `Image`; + } else if (image.type.startsWith('video')) { + content = ``; + }else if (image.type.startsWith('audio')) { + content = `