diff --git a/.editorconfig b/.editorconfig index 74fb9e0..f46a996 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,6 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf + +[*.php] +indent_size = 4 diff --git a/.gitignore b/.gitignore index 716c53e..4d6e2ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .vscode !.vscode/settings.json +dist node_modules +dbSettings.js diff --git a/README.md b/README.md index 32f1426..c99cba0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,19 @@ # ssn-auto -A tool that regularly checks for new patches and will automatically download, install and process them. Designed to be deployed on the Jedipedia server and may be difficult to set up in other environments. If you want to build your own patching tool, start with the [ssn-tools](/swtor/ssn-tools) repository. +A tool that regularly checks for new patches and will automatically download, install and process them. Designed to be deployed on the Jedipedia server and not supported to be set up in other environments, so we offer limited support. If you want to build your own patching tool, start with the [ssn-tools](/swtor/ssn-tools) repository. + +## Installation + +After downloading this repository, create a file called `dbSettings.js` with the following contents, putting in the database settings as required: + +```js +module.exports = { + host: 'localhost', + user: 'INSERT_USER_NAME', + password: 'INSERT_PASSWORD', + database: 'INSERT_DATABASE_NAME', +}; +``` ## License diff --git a/dbSettings.d.ts b/dbSettings.d.ts new file mode 100644 index 0000000..473a57c --- /dev/null +++ b/dbSettings.d.ts @@ -0,0 +1,4 @@ +export const host: string; +export const user: string; +export const password: string; +export const database: string; diff --git a/package-lock.json b/package-lock.json index d8a384e..3520613 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,4 +1,119 @@ { "name": "ssn-auto", - "lockfileVersion": 1 + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@types/mysql": { + "version": "2.15.5", + "resolved": "https://registry.npmjs.org/@types/mysql/-/mysql-2.15.5.tgz", + "integrity": "sha512-4QAISTUGZbcFh7bqdndo08xRdES5OTU+JODy8VCZbe1yiXyGjqw1H83G43XjQ3IbC10wn9xlGd44A5RXJwNh0Q==", + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "10.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.2.tgz", + "integrity": "sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ==" + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "mysql": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/mysql/-/mysql-2.16.0.tgz", + "integrity": "sha512-dPbN2LHonQp7D5ja5DJXNbCLe/HRdu+f3v61aguzNRQIrmZLOeRoymBYyeThrR6ug+FqzDL95Gc9maqZUJS+Gw==", + "requires": { + "bignumber.js": "4.1.0", + "readable-stream": "2.3.6", + "safe-buffer": "5.1.2", + "sqlstring": "2.3.1" + } + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "sqlstring": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", + "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" + }, + "ssn": { + "version": "git+https://git.jedipedia.net/swtor/ssn.git#5caa3a935d8a1dda040fbe2f7d63114213b01e81", + "from": "git+https://git.jedipedia.net/swtor/ssn.git", + "requires": { + "@types/node": "^10.12.2", + "ssn-installer": "git+https://git.jedipedia.net/swtor/ssn-installer.git#488e49b6b234caca0bfbc3f746fa16abdc002dea", + "xml-js": "^1.6.8" + } + }, + "ssn-installer": { + "version": "git+https://git.jedipedia.net/swtor/ssn-installer.git#488e49b6b234caca0bfbc3f746fa16abdc002dea", + "from": "git+https://git.jedipedia.net/swtor/ssn-installer.git" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "xml-js": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.8.tgz", + "integrity": "sha512-kUv/geyN80d+s1T68uBfjoz+PjNUjwwf5AWWRwKRqqQaGozpMVsFsKYnenPsxlbN/VL7f0ia8NfLLPCDwX+95Q==", + "requires": { + "sax": "^1.2.4" + } + } + } } diff --git a/package.json b/package.json index 3c5aec7..6b0d25e 100644 --- a/package.json +++ b/package.json @@ -6,9 +6,16 @@ "type": "git", "url": "https://git.jedipedia.net/swtor/ssn-auto.git" }, - "main": "src/index.php", + "main": "dist/index.js", + "types": "dist/index.d.ts", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "start": "rm -rf dist && tsc", + "test": "echo \"Error: no test specified\" && exit 1", + "update": "rm -rf node_modules package-lock.json && npm install" }, - "dependencies": {} + "dependencies": { + "@types/mysql": "^2.15.5", + "mysql": "^2.16.0", + "ssn": "git+https://git.jedipedia.net/swtor/ssn.git" + } } diff --git a/src/getConfig.ts b/src/getConfig.ts new file mode 100644 index 0000000..5023a51 --- /dev/null +++ b/src/getConfig.ts @@ -0,0 +1,28 @@ +import { Product } from 'ssn'; +import IProductData from './interfaces/IProductData'; + +const data: { [key in Product]: IProductData } = { + assets_swtor_main: { manifestVersion: 294, httpVersion: 294 }, + assets_swtor_de_de: { manifestVersion: 297, httpVersion: 297 }, + assets_swtor_en_us: { manifestVersion: 299, httpVersion: 299 }, + assets_swtor_fr_fr: { manifestVersion: 299, httpVersion: 299 }, + assets_swtor_test_main: { manifestVersion: 292, httpVersion: 292 }, + assets_swtor_test_de_de: { manifestVersion: 295, httpVersion: 295 }, + assets_swtor_test_en_us: { manifestVersion: 295, httpVersion: 295 }, + assets_swtor_test_fr_fr: { manifestVersion: 295, httpVersion: 295 }, + retailclient_swtor: { manifestVersion: 247, httpVersion: 247 }, + retailclient_publictest: { manifestVersion: 269, httpVersion: 269 }, + retailclient_betatest: { manifestVersion: 13, httpVersion: 13 }, + retailclient_liveqatest: { manifestVersion: 203, httpVersion: 205 }, + retailclient_liveeptest: { manifestVersion: 133, httpVersion: 199 }, + retailclient_cstraining: { manifestVersion: 141, httpVersion: 141 }, + retailclient_squadron157: { manifestVersion: 11, httpVersion: -1 }, + eualas: { manifestVersion: 0, httpVersion: 0 }, + patcher2014: { manifestVersion: 8, httpVersion: 7 }, + patcher2017: { manifestVersion: 0, httpVersion: 0 }, + movies_de_de: { manifestVersion: 5, httpVersion: 5 }, + movies_en_us: { manifestVersion: 5, httpVersion: 5 }, + movies_fr_fr: { manifestVersion: 5, httpVersion: 5 }, +}; + +export default data; diff --git a/src/index.php b/src/index.php deleted file mode 100644 index 0615691..0000000 --- a/src/index.php +++ /dev/null @@ -1,3 +0,0 @@ - { + database.exit(); +}); + +/*//for each product, check new versions +Object.entries(products).forEach(([product, { manifestVersion, httpVersion }]: [string, IProductData]) => { + const curVersion = 100; + //Check patch to next release + for (let i = curVersion; i < curVersion + 3; i += 1) { + getSolidpkg(product as Product, i, i + 1).then((result) => { + console.log(result); + newPatches.push({ product, version: i, result }); + }).catch(() => { + //do nothing + }); + } + //TODO +});*/ + +//parse results +//TODO: need to wait for all promises to be complete + +//if new version was found, try to install it + +//also check manifest in case a new version is available for installation. remember time when patch became available for installation but otherwise do nothing + +//delete lock file diff --git a/src/interfaces/IProductData.ts b/src/interfaces/IProductData.ts new file mode 100644 index 0000000..6fbdbdb --- /dev/null +++ b/src/interfaces/IProductData.ts @@ -0,0 +1,6 @@ +export default interface IProductData { + /** Latest release number of this product according to manifest. */ + manifestVersion: number; + /** Latest release number of this product available from the patch server. */ + httpVersion: number; +} diff --git a/src/logger/writeLog.ts b/src/logger/writeLog.ts new file mode 100644 index 0000000..da64f4b --- /dev/null +++ b/src/logger/writeLog.ts @@ -0,0 +1,3 @@ +export default function writeLog(message: string, severity: number = 0) { + console.warn(message); +} diff --git a/src/model/createStatement.ts b/src/model/createStatement.ts new file mode 100644 index 0000000..5975371 --- /dev/null +++ b/src/model/createStatement.ts @@ -0,0 +1,9 @@ +//Generated by `SHOW CREATE TABLE ssn_versions` +const createStatement = `CREATE TABLE \`ssn_versions\` ( + \`product\` tinytext COLLATE utf8mb4_unicode_520_ci NOT NULL, + \`manifest\` smallint(6) NOT NULL DEFAULT '-1', + \`http\` smallint(6) NOT NULL DEFAULT '-1', + PRIMARY KEY (\`product\`(25)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci`; + +export default createStatement; diff --git a/src/model/database.ts b/src/model/database.ts new file mode 100644 index 0000000..f065853 --- /dev/null +++ b/src/model/database.ts @@ -0,0 +1,30 @@ +import * as mysql from 'mysql'; +import * as dbSettings from '../../dbSettings'; + +const connection = mysql.createConnection(dbSettings); + +export function init() { + return new Promise((resolve, reject) => { + connection.connect(function(err) { + if (err) { + reject(err); + } + resolve(); + }); + }); +} + +export function query(command: string): Promise<{ results: any, fields?: mysql.FieldInfo[] }> { + return new Promise((resolve, reject) => { + connection.query(command, function(error, results, fields) { + if (error !== null) { + reject(error); + } + resolve({ results, fields }); + }); + }); +} + +export function exit(): void { + connection.end(); +} diff --git a/src/model/model.ts b/src/model/model.ts new file mode 100644 index 0000000..994b5fe --- /dev/null +++ b/src/model/model.ts @@ -0,0 +1,39 @@ +import { Product } from 'ssn'; +import * as database from './database'; +import products from './products'; +import writeLog from '../logger/writeLog'; +import createStatement from './createStatement'; + +export async function init() { + await database.init(); + + //Only create table if it does not exist yet + const createIfNotExists = createStatement.replace(/^CREATE TABLE/, 'CREATE TABLE IF NOT EXISTS'); + await database.query(createIfNotExists); + + /**Products that are missing from the database and need to be inserted. */ + const missingProducts: Product[] = []; + /** Products inside the database table that are not valid products. Will be printed to error log and ignored */ + const erroneousProducts: Product[] = []; + + const { results: existingRows } = await database.query('SELECT * FROM ssn_versions'); + console.log(existingRows); + return; + + existingRows.forEach((row: any) => { + //TODO: match with products + }); + + //If row does not exist, INSERT into table + await Promise.all(missingProducts.map((product) => { + return database.query(`INSERT INTO ssn_versions ${product}`); + })); + + //If row exists that is not in products, ignore it and output error + erroneousProducts.forEach((product) => { + writeLog(`Product ${product} in database table is invalid.`); + }); + + //TODO: insert products + +} diff --git a/src/model/products.ts b/src/model/products.ts new file mode 100644 index 0000000..1fd80a5 --- /dev/null +++ b/src/model/products.ts @@ -0,0 +1,27 @@ +import { Product } from 'ssn'; + +const productList: Product[] = [ + 'assets_swtor_de_de', + 'assets_swtor_en_us', + 'assets_swtor_fr_fr', + 'assets_swtor_main', + 'assets_swtor_test_de_de', + 'assets_swtor_test_en_us', + 'assets_swtor_test_fr_fr', + 'assets_swtor_test_main', + 'eualas', + 'movies_de_de', + 'movies_en_us', + 'movies_fr_fr', + 'patcher2014', + 'patcher2017', + 'retailclient_betatest', + 'retailclient_cstraining', + 'retailclient_liveeptest', + 'retailclient_liveqatest', + 'retailclient_publictest', + 'retailclient_squadron157', + 'retailclient_swtor', +]; + +export default productList; diff --git a/src/tslint.json b/src/tslint.json new file mode 100644 index 0000000..188e8af --- /dev/null +++ b/src/tslint.json @@ -0,0 +1,18 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended", + ], + "jsRules": {}, + "rules": { + "comment-format": false, + "indent": [true, "spaces", 2], + "max-line-length": false, + "no-bitwise": false, + "no-console": false, + "no-invalid-this": [true, "check-function-in-method"], + "object-literal-sort-keys": [true, "match-declaration-order"], + "quotemark": [true, "single"], + }, + "rulesDirectory": [], +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5129718 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "declaration": true, + "inlineSourceMap": true, + "inlineSources": true, + "module": "commonjs", + "moduleResolution": "node", + "newLine": "lf", + "outDir": "dist", + "paths": { + "*": [ + "node_modules/*" + ] + }, + "strict": true, + "target": "es2018", + }, + "include": [ + "src/**/*" + ] +}