Initial commit with decorator router, and some basic subsonic api code
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
.idea
|
||||
node_modules
|
||||
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
import '@tsmetadata/polyfill';
|
||||
import Koa from 'koa';
|
||||
import {SubsonicRouter} from "./src/subsonic/router";
|
||||
|
||||
try {
|
||||
const app = new Koa();
|
||||
(new SubsonicRouter()).injectInto(app);
|
||||
|
||||
app.listen(8080);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
Generated
+726
@@ -0,0 +1,726 @@
|
||||
{
|
||||
"name": "jukesquare-backend",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "jukesquare-backend",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@koa/router": "^15.4.0",
|
||||
"@tsmetadata/polyfill": "^1.1.3",
|
||||
"koa": "^3.2.0",
|
||||
"xmlbuilder2": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/koa": "^3.0.2",
|
||||
"typescript": "^6.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@koa/router": {
|
||||
"version": "15.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@koa/router/-/router-15.4.0.tgz",
|
||||
"integrity": "sha512-vKYlXtoCfcAN8z4dHiveYX55rTYOgHEYJNumK1WM9ZAwaArhreGVkyC1LTMGfUQUJyIO/SbwRFBOHeOCY8/MaQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.3",
|
||||
"http-errors": "^2.0.1",
|
||||
"koa-compose": "^4.1.0",
|
||||
"path-to-regexp": "^8.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"koa": "^2.0.0 || ^3.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"koa": {
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@oozcitak/dom": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/dom/-/dom-2.0.2.tgz",
|
||||
"integrity": "sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oozcitak/infra": "^2.0.2",
|
||||
"@oozcitak/url": "^3.0.0",
|
||||
"@oozcitak/util": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oozcitak/infra": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/infra/-/infra-2.0.2.tgz",
|
||||
"integrity": "sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oozcitak/util": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oozcitak/url": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/url/-/url-3.0.0.tgz",
|
||||
"integrity": "sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oozcitak/infra": "^2.0.2",
|
||||
"@oozcitak/util": "^10.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@oozcitak/util": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@oozcitak/util/-/util-10.0.0.tgz",
|
||||
"integrity": "sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tsmetadata/polyfill": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsmetadata/polyfill/-/polyfill-1.1.3.tgz",
|
||||
"integrity": "sha512-uSRn4aPO4F3wlSG/cPCAvclF9Sxf01OizWfWbSoSYsSHAK8LnYdua9iJAm7v2ePUrGWMP4ZCn9QjniZEKZHyFg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/content-disposition": {
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.9.tgz",
|
||||
"integrity": "sha512-8uYXI3Gw35MhiVYhG3s295oihrxRyytcRHjSjqnqZVDDy/xcGBRny7+Xj1Wgfhv5QzRtN2hB2dVRBUX9XW3UcQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/cookies": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.9.2.tgz",
|
||||
"integrity": "sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/express": "*",
|
||||
"@types/keygrip": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
|
||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
|
||||
"integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-assert": {
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.6.tgz",
|
||||
"integrity": "sha512-TTEwmtjgVbYAzZYWyeHPrrtWnfVkm8tQkP8P21uQifPgMRgjrow3XDEYqucuC8SKZJT7pUnhU/JymvjggxO9vw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/keygrip": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz",
|
||||
"integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/koa": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa/-/koa-3.0.2.tgz",
|
||||
"integrity": "sha512-7TRzVOBcH/q8CfPh9AmHBQ8TZtimT4Sn+rw8//hXveI6+F41z93W8a+0B0O8L7apKQv+vKBIEZSECiL0Oo1JFA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/accepts": "*",
|
||||
"@types/content-disposition": "*",
|
||||
"@types/cookies": "*",
|
||||
"@types/http-assert": "*",
|
||||
"@types/http-errors": "^2",
|
||||
"@types/keygrip": "*",
|
||||
"@types/koa-compose": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/koa-compose": {
|
||||
"version": "3.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.9.tgz",
|
||||
"integrity": "sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/koa": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz",
|
||||
"integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz",
|
||||
"integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
||||
"integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
|
||||
"integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/argparse": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz",
|
||||
"integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookies": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz",
|
||||
"integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"keygrip": "~1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz",
|
||||
"integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/delegates": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
|
||||
"integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/http-assert": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz",
|
||||
"integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"deep-equal": "~1.0.1",
|
||||
"http-errors": "~1.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/http-assert/node_modules/depd": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
|
||||
"integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/http-assert/node_modules/http-errors": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
|
||||
"integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~1.1.2",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": ">= 1.5.0 < 2",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/http-assert/node_modules/statuses": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
|
||||
"integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/keygrip": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
|
||||
"integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tsscmp": "1.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/koa": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/koa/-/koa-3.2.0.tgz",
|
||||
"integrity": "sha512-TrM4/tnNY7uJ1aW55sIIa+dqBvc4V14WRIAlGcWat9wV5pRS9Wr5Zk2ZTjQP1jtfIHDoHiSbPuV08P0fUZo2pg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"accepts": "^1.3.8",
|
||||
"content-disposition": "~1.0.1",
|
||||
"content-type": "^1.0.5",
|
||||
"cookies": "~0.9.1",
|
||||
"delegates": "^1.0.0",
|
||||
"destroy": "^1.2.0",
|
||||
"encodeurl": "^2.0.0",
|
||||
"escape-html": "^1.0.3",
|
||||
"fresh": "~0.5.2",
|
||||
"http-assert": "^1.5.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"koa-compose": "^4.1.0",
|
||||
"mime-types": "^3.0.1",
|
||||
"on-finished": "^2.4.1",
|
||||
"parseurl": "^1.3.3",
|
||||
"statuses": "^2.0.1",
|
||||
"type-is": "^2.0.1",
|
||||
"vary": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/koa-compose": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz",
|
||||
"integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
||||
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz",
|
||||
"integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "^1.54.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "8.4.2",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
|
||||
"integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/tsscmp": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz",
|
||||
"integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.x"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
||||
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"content-type": "^1.0.5",
|
||||
"media-typer": "^1.1.0",
|
||||
"mime-types": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
|
||||
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.18.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder2": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder2/-/xmlbuilder2-4.0.3.tgz",
|
||||
"integrity": "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oozcitak/dom": "^2.0.2",
|
||||
"@oozcitak/infra": "^2.0.2",
|
||||
"@oozcitak/util": "^10.0.0",
|
||||
"js-yaml": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "jukesquare-backend",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.brokencube.co.uk/brokencube/jukesquare-backend.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": "",
|
||||
"type": "module",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@koa/router": "^15.4.0",
|
||||
"@tsmetadata/polyfill": "^1.1.3",
|
||||
"koa": "^3.2.0",
|
||||
"xmlbuilder2": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/koa": "^3.0.2",
|
||||
"typescript": "^6.0.2"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import Router from "@koa/router";
|
||||
import Koa from "koa";
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
type Route = {
|
||||
method: string;
|
||||
path: string;
|
||||
fn: any;
|
||||
};
|
||||
|
||||
export class BaseRouter {
|
||||
public router!: Router;
|
||||
|
||||
processRoutes(routes: Route[]) {
|
||||
for (const route of routes) switch (route.method) {
|
||||
case "get": this.router.get(route.path, route.fn); break;
|
||||
case "post": this.router.post(route.path, route.fn); break;
|
||||
case 'use': this.router.use(route.path, route.fn); break;
|
||||
}
|
||||
}
|
||||
|
||||
injectInto(app: Koa) {
|
||||
app.use(this.router.routes()).use(this.router.allowedMethods());
|
||||
}
|
||||
}
|
||||
|
||||
export function get(path: string) {
|
||||
return function getDecorator (originalMethod: any, context: ClassMethodDecoratorContext) {
|
||||
if (context.metadata) {
|
||||
if (!context.metadata.routes) {
|
||||
context.metadata.routes = [];
|
||||
}
|
||||
// @ts-ignore
|
||||
context.metadata.routes.push({method: 'get', path, fn: originalMethod});
|
||||
}
|
||||
return originalMethod;
|
||||
}
|
||||
}
|
||||
|
||||
export function post(path: string) {
|
||||
return function getDecorator (originalMethod: any, context: ClassMethodDecoratorContext) {
|
||||
if (context.metadata) {
|
||||
if (!context.metadata.routes) {
|
||||
context.metadata.routes = [];
|
||||
}
|
||||
// @ts-ignore
|
||||
context.metadata.routes.push({method: 'post', path, fn: originalMethod});
|
||||
}
|
||||
return originalMethod;
|
||||
}
|
||||
}
|
||||
|
||||
export function middleware(fn: any, path: string = '') {
|
||||
return function middlewareDecorator (constructor: any, context: ClassDecoratorContext) {
|
||||
if (context.metadata) {
|
||||
if (!context.metadata.routes) {
|
||||
context.metadata.routes = [];
|
||||
}
|
||||
// @ts-ignore
|
||||
context.metadata.routes.unshift({method: 'use', path, fn});
|
||||
}
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
|
||||
export function router(prefix: string) {
|
||||
return function routerDecorator<T extends Constructor>(constructor: T, context: ClassDecoratorContext<T>) {
|
||||
return class extends constructor {
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
if (this instanceof BaseRouter) {
|
||||
this.router = new Router({prefix});
|
||||
// @ts-ignore
|
||||
this.processRoutes(context.metadata?.routes || [] satisfies Route[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export enum SubsonicErrorCode {
|
||||
Generic = 0,
|
||||
RequiredParam = 10,
|
||||
ClientUpgradeRequired = 20,
|
||||
ServerUpgradeRequired = 30,
|
||||
InvalidAuth = 40,
|
||||
TokenAuthNotSupported = 41,
|
||||
SpecifiedAuthNotSupported = 42,
|
||||
ConflictingAuthSchemes = 43,
|
||||
InvalidApikey = 44,
|
||||
NotAuthorized = 50,
|
||||
NotFound = 70,
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export enum SubsonicResponseFormat {
|
||||
XML,
|
||||
JSON,
|
||||
JSONP
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './SubsonicErrorCode';
|
||||
export * from './SubsonicResponseFormat';
|
||||
@@ -0,0 +1,133 @@
|
||||
import * as crypto from "node:crypto";
|
||||
import {SubsonicResponseFormat, SubsonicErrorCode} from "../enum";
|
||||
import Koa from "koa";
|
||||
import {SubsonicError} from "./error";
|
||||
|
||||
export type SubsonicContextPrimitive = {
|
||||
format: SubsonicResponseFormat;
|
||||
apikey: string | null;
|
||||
version: string | null;
|
||||
client: string | null;
|
||||
callbackName: string | null;
|
||||
};
|
||||
|
||||
export const STATIC_SUBSONIC_PASSWORD = 'apikey';
|
||||
|
||||
export class SubsonicContext {
|
||||
constructor(
|
||||
public readonly format: SubsonicResponseFormat,
|
||||
public readonly apikey: string | null = null,
|
||||
public readonly version: string | null = null,
|
||||
public readonly client: string | null = null,
|
||||
public readonly callbackName: string | null = null,
|
||||
) {
|
||||
if (this.version === null) this.version = '1.1.0';
|
||||
if (!this.callbackName) this.callbackName = 'parseResponse';
|
||||
}
|
||||
|
||||
static fromQueryParams(req: Koa.Request) {
|
||||
const queryParams = SubsonicContext.emptyPrimitive();
|
||||
try {
|
||||
if (req.query['f'] === 'json') {
|
||||
queryParams.format = SubsonicResponseFormat.JSON;
|
||||
}
|
||||
|
||||
if (req.query['f'] === 'jsonp') {
|
||||
queryParams.format = SubsonicResponseFormat.JSONP;
|
||||
if (req.query['callback'] && !Array.isArray(req.query['callback'])) {
|
||||
queryParams.callbackName = req.query['callback'];
|
||||
}
|
||||
}
|
||||
|
||||
if (!req.query['v'] || Array.isArray(req.query['v'])) {
|
||||
throw new Error('No api version provided');
|
||||
}
|
||||
queryParams.version = req.query['v'];
|
||||
|
||||
if (req.query['apikey']) {
|
||||
if (req.query['u'] || req.query['t'] || req.query['s'] || req.query['p']) {
|
||||
throw new SubsonicError(SubsonicErrorCode.ConflictingAuthSchemes, 'Cannot provide params "p", "t", "s", "u" when specifying "apikey"');
|
||||
}
|
||||
if (Array.isArray(req.query['apikey'])) {
|
||||
throw new SubsonicError(SubsonicErrorCode.ConflictingAuthSchemes, 'Multiple "apikey" params provided');
|
||||
}
|
||||
queryParams.apikey = req.query['apikey'];
|
||||
} else {
|
||||
if (req.query['p']) {
|
||||
throw new SubsonicError(SubsonicErrorCode.SpecifiedAuthNotSupported, 'Subsonic API password authentication not supported in JukeSquare');
|
||||
}
|
||||
|
||||
if (!req.query['t'] || Array.isArray(req.query['t'])) {
|
||||
throw new SubsonicError(SubsonicErrorCode.InvalidAuth, '(Legacy Auth) No token provided');
|
||||
}
|
||||
|
||||
if (!req.query['s'] || Array.isArray(req.query['s'])) {
|
||||
throw new SubsonicError(SubsonicErrorCode.InvalidAuth, '(Legacy Auth) No token salt provided');
|
||||
}
|
||||
|
||||
/* For backwards compatability with older Subsonic clients, we support "user and password" auth using
|
||||
* a valid apikey as the username and static string "apikey" as the password.
|
||||
* We force this specific password to allow dropping out very early if we suspect a real username/password pair have been provided.
|
||||
* Effectively this is a way of "opting-in" to legacy auth support.
|
||||
*/
|
||||
const expectedToken = SubsonicContext.hashToken(STATIC_SUBSONIC_PASSWORD, req.query['s']);
|
||||
if (req.query['t'] !== expectedToken) {
|
||||
throw new SubsonicError(SubsonicErrorCode.InvalidAuth, '(Legacy Auth) Token password was not provided as "apikey" - Subsonic Api support in JukeSquare requires apikey to be provided as username and "apikey" provided as password');
|
||||
}
|
||||
|
||||
if (!req.query['u'] || Array.isArray(req.query['u'])) {
|
||||
throw new SubsonicError(SubsonicErrorCode.InvalidAuth, '(Legacy Auth) No user provided - user\'s apikey must be provided in username field');
|
||||
}
|
||||
queryParams.apikey = req.query['u'];
|
||||
}
|
||||
|
||||
if (!req.query['c'] || Array.isArray(req.query['c'])) {
|
||||
throw new SubsonicError(SubsonicErrorCode.InvalidAuth, 'No client name provided');
|
||||
}
|
||||
queryParams.client = req.query['c'];
|
||||
|
||||
return SubsonicContext.fromPrimitive(queryParams);
|
||||
} catch (e) {
|
||||
/* Inject whatever context we have gathered into the error before bubbling up */
|
||||
if (e instanceof SubsonicError) {
|
||||
e.setContext(SubsonicContext.fromPrimitive(queryParams));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static empty(): SubsonicContext {
|
||||
return new SubsonicContext(
|
||||
SubsonicResponseFormat.XML,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
static fromPrimitive(primitive: SubsonicContextPrimitive): SubsonicContext {
|
||||
return new SubsonicContext(
|
||||
primitive.format,
|
||||
primitive.apikey,
|
||||
primitive.version,
|
||||
primitive.client,
|
||||
primitive.callbackName,
|
||||
);
|
||||
}
|
||||
|
||||
static emptyPrimitive(): SubsonicContextPrimitive {
|
||||
return {
|
||||
format: SubsonicResponseFormat.XML,
|
||||
apikey: null,
|
||||
version: null,
|
||||
client: null,
|
||||
callbackName: null,
|
||||
} satisfies SubsonicContextPrimitive;
|
||||
}
|
||||
|
||||
static hashToken(token: string, salt: string): string {
|
||||
return crypto.createHash('md5').update(`${token}${salt}`).digest('hex');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import {SubsonicErrorCode} from "../enum";
|
||||
import {SubsonicContext} from "./context";
|
||||
|
||||
export class SubsonicError extends Error {
|
||||
constructor(
|
||||
public code: SubsonicErrorCode,
|
||||
public message: string = '',
|
||||
public context: SubsonicContext | null = null,
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public setContext(context: SubsonicContext): void {
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import Koa from "koa";
|
||||
import {SubsonicContext} from "../context";
|
||||
|
||||
export async function extractSubsonicApiContext(ctx: Koa.Context, next: () => Promise<any>) {
|
||||
ctx.subsonicContext = SubsonicContext.fromQueryParams(ctx.request);
|
||||
await next();
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './subsonicErrorHandler';
|
||||
export * from './extractSubsonicApiContext';
|
||||
@@ -0,0 +1,35 @@
|
||||
import {SubsonicError} from "../error";
|
||||
import Koa from "koa";
|
||||
import {SubsonicErrorCode} from "../../enum/SubsonicErrorCode";
|
||||
import {SubsonicContext} from "../context";
|
||||
import {Renderer} from "../renderer";
|
||||
|
||||
export async function subsonicErrorHandler(ctx: Koa.Context, next: () => Promise<any>) {
|
||||
try {
|
||||
await next();
|
||||
} catch (e) {
|
||||
if (e instanceof SubsonicError) {
|
||||
ctx.body = Renderer.renderError(e);
|
||||
return;
|
||||
}
|
||||
const context = ctx.store?.subsonicContext ?? SubsonicContext.empty();
|
||||
|
||||
if (e instanceof Error) {
|
||||
const genericError = new SubsonicError(
|
||||
SubsonicErrorCode.Generic,
|
||||
`Generic Error: ${e.message}`,
|
||||
context
|
||||
);
|
||||
|
||||
ctx.body = Renderer.renderError(genericError);
|
||||
return;
|
||||
}
|
||||
|
||||
const unknownError = new SubsonicError(
|
||||
SubsonicErrorCode.Generic,
|
||||
`Unknown Error - JSON representation of error object returned: ${JSON.stringify(e)}`,
|
||||
context
|
||||
);
|
||||
ctx.body = Renderer.renderError(unknownError);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './xml-responder';
|
||||
export * from './json-responder';
|
||||
export * from './jsonp-responder';
|
||||
@@ -0,0 +1,20 @@
|
||||
import {SubsonicErrorCode} from "../../enum/SubsonicErrorCode";
|
||||
|
||||
export class JsonResponder {
|
||||
static renderError(code: SubsonicErrorCode, message: string): string {
|
||||
return JSON.stringify({
|
||||
"subsonic-response": {
|
||||
status: "failed",
|
||||
version: "1.16.1",
|
||||
error:{
|
||||
code,
|
||||
message
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static createResponse(element: string | null): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import {SubsonicErrorCode} from "../../enum/SubsonicErrorCode";
|
||||
import {JsonResponder} from "./json-responder";
|
||||
|
||||
export class JsonPResponder {
|
||||
static renderError(code: SubsonicErrorCode, message: string, callback: string): string {
|
||||
const json = JsonResponder.renderError(code, message);
|
||||
return `${callback}(${json});`;
|
||||
}
|
||||
|
||||
static createResponse(element: string | null): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import {create} from 'xmlbuilder2';
|
||||
import {SubsonicErrorCode} from "../../enum/SubsonicErrorCode";
|
||||
|
||||
export class XmlResponder {
|
||||
private static buildRoot(ok: boolean = true) {
|
||||
return create({version: '1.0', encoding: 'utf-8'})
|
||||
.ele('subsonic-response', {
|
||||
xmlns: 'http://subsonic.org/restapi',
|
||||
status: ok ? 'ok' : 'failed',
|
||||
version: "1.16.1",
|
||||
})
|
||||
}
|
||||
|
||||
static renderError(code: SubsonicErrorCode, message: string) {
|
||||
return XmlResponder.buildRoot( false)
|
||||
.ele('error', {
|
||||
code,
|
||||
message,
|
||||
})
|
||||
.end({prettyPrint: true});
|
||||
}
|
||||
|
||||
static createResponse(element: object | null): any {
|
||||
const root = XmlResponder.buildRoot();
|
||||
if (element) {}
|
||||
|
||||
return root.end({prettyPrint: true});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import {SubsonicResponseFormat} from "../enum";
|
||||
import {SubsonicError} from "./error";
|
||||
import {SubsonicContext} from "./context";
|
||||
import {JsonPResponder, JsonResponder, XmlResponder} from "./renderBackends";
|
||||
|
||||
export class Renderer extends Error {
|
||||
static render(message: object | null, context: SubsonicContext | null = null) {
|
||||
switch (context?.format) {
|
||||
case SubsonicResponseFormat.XML:
|
||||
default:
|
||||
return XmlResponder.createResponse(message);
|
||||
case SubsonicResponseFormat.JSON:
|
||||
return '';
|
||||
case SubsonicResponseFormat.JSONP:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
static renderError(e: SubsonicError) {
|
||||
switch (e.context?.format) {
|
||||
case SubsonicResponseFormat.XML:
|
||||
default:
|
||||
return XmlResponder.renderError(e.code, e.message);
|
||||
case SubsonicResponseFormat.JSON:
|
||||
return JsonResponder.renderError(e.code, e.message);
|
||||
case SubsonicResponseFormat.JSONP:
|
||||
return JsonPResponder.renderError(e.code, e.message, e.context?.callbackName || 'parseResponse');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import {BaseRouter, get, middleware, post, router} from "../base/router";
|
||||
import Koa from "koa";
|
||||
import {extractSubsonicApiContext, subsonicErrorHandler} from "./middleware";
|
||||
import {Renderer} from "./renderer";
|
||||
|
||||
@router('/rest')
|
||||
@middleware(subsonicErrorHandler)
|
||||
@middleware(extractSubsonicApiContext)
|
||||
export class SubsonicRouter extends BaseRouter {
|
||||
@get('/ping')
|
||||
ping(ctx: Koa.Context) {
|
||||
console.log('ping');
|
||||
ctx.body = Renderer.render(null, ctx.subsonicContext)
|
||||
}
|
||||
|
||||
@get('/ping2')
|
||||
@post('/ping2')
|
||||
ping2(ctx: Koa.Context) {
|
||||
console.log('ping');
|
||||
ctx.body = Renderer.render(null, ctx.subsonicContext)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user