export cards
This commit is contained in:
+26
@@ -167,6 +167,23 @@
|
|||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
||||||
|
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -745,6 +762,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
(The MIT License)
|
||||||
|
|
||||||
|
Copyright (c) 2013 Troy Goode <troygoode@gmail.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
'Software'), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||||
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||||
|
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+277
@@ -0,0 +1,277 @@
|
|||||||
|
# cors
|
||||||
|
|
||||||
|
[![NPM Version][npm-image]][npm-url]
|
||||||
|
[![NPM Downloads][downloads-image]][downloads-url]
|
||||||
|
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
||||||
|
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||||
|
|
||||||
|
CORS is a [Node.js](https://nodejs.org/en/) middleware for [Express](https://expressjs.com/)/[Connect](https://github.com/senchalabs/connect) that sets [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) response headers. These headers tell browsers which origins can read responses from your server.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> **How CORS Works:** This package sets response headers—it doesn't block requests. CORS is enforced by browsers: they check the headers and decide if JavaScript can read the response. Non-browser clients (curl, Postman, other servers) ignore CORS entirely. See the [MDN CORS guide](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) for details.
|
||||||
|
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Simple Usage](#simple-usage-enable-all-cors-requests)
|
||||||
|
* [Enable CORS for a Single Route](#enable-cors-for-a-single-route)
|
||||||
|
* [Configuring CORS](#configuring-cors)
|
||||||
|
* [Configuring CORS w/ Dynamic Origin](#configuring-cors-w-dynamic-origin)
|
||||||
|
* [Enabling CORS Pre-Flight](#enabling-cors-pre-flight)
|
||||||
|
* [Customizing CORS Settings Dynamically per Request](#customizing-cors-settings-dynamically-per-request)
|
||||||
|
* [Configuration Options](#configuration-options)
|
||||||
|
* [Common Misconceptions](#common-misconceptions)
|
||||||
|
* [License](#license)
|
||||||
|
* [Original Author](#original-author)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||||
|
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||||
|
[`npm install` command](https://docs.npmjs.com/downloading-and-installing-packages-locally):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ npm install cors
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Simple Usage (Enable *All* CORS Requests)
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
// Adds headers: Access-Control-Allow-Origin: *
|
||||||
|
app.use(cors())
|
||||||
|
|
||||||
|
app.get('/products/:id', function (req, res, next) {
|
||||||
|
res.json({msg: 'Hello'})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable CORS for a Single Route
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
// Adds headers: Access-Control-Allow-Origin: *
|
||||||
|
app.get('/products/:id', cors(), function (req, res, next) {
|
||||||
|
res.json({msg: 'Hello'})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuring CORS
|
||||||
|
|
||||||
|
See the [configuration options](#configuration-options) for details.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
var corsOptions = {
|
||||||
|
origin: 'http://example.com',
|
||||||
|
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds headers: Access-Control-Allow-Origin: http://example.com, Vary: Origin
|
||||||
|
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
|
||||||
|
res.json({msg: 'Hello'})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuring CORS w/ Dynamic Origin
|
||||||
|
|
||||||
|
This module supports validating the origin dynamically using a function provided
|
||||||
|
to the `origin` option. This function will be passed a string that is the origin
|
||||||
|
(or `undefined` if the request has no origin), and a `callback` with the signature
|
||||||
|
`callback(error, origin)`.
|
||||||
|
|
||||||
|
The `origin` argument to the callback can be any value allowed for the `origin`
|
||||||
|
option of the middleware, except a function. See the
|
||||||
|
[configuration options](#configuration-options) section for more information on all
|
||||||
|
the possible value types.
|
||||||
|
|
||||||
|
This function is designed to allow the dynamic loading of allowed origin(s) from
|
||||||
|
a backing datasource, like a database.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
var corsOptions = {
|
||||||
|
origin: function (origin, callback) {
|
||||||
|
// db.loadOrigins is an example call to load
|
||||||
|
// a list of origins from a backing database
|
||||||
|
db.loadOrigins(function (error, origins) {
|
||||||
|
callback(error, origins)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds headers: Access-Control-Allow-Origin: <matched origin>, Vary: Origin
|
||||||
|
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
|
||||||
|
res.json({msg: 'Hello'})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enabling CORS Pre-Flight
|
||||||
|
|
||||||
|
Certain CORS requests are considered 'complex' and require an initial
|
||||||
|
`OPTIONS` request (called the "pre-flight request"). An example of a
|
||||||
|
'complex' CORS request is one that uses an HTTP verb other than
|
||||||
|
GET/HEAD/POST (such as DELETE) or that uses custom headers. To enable
|
||||||
|
pre-flighting, you must add a new OPTIONS handler for the route you want
|
||||||
|
to support:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var express = require('express')
|
||||||
|
var cors = require('cors')
|
||||||
|
var app = express()
|
||||||
|
|
||||||
|
app.options('/products/:id', cors()) // preflight for DELETE
|
||||||
|
app.del('/products/:id', cors(), function (req, res, next) {
|
||||||
|
res.json({msg: 'Hello'})
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also enable pre-flight across-the-board like so:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
app.options('*', cors()) // include before other routes
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: When using this middleware as an application level middleware (for
|
||||||
|
example, `app.use(cors())`), pre-flight requests are already handled for all
|
||||||
|
routes.
|
||||||
|
|
||||||
|
### Customizing CORS Settings Dynamically per Request
|
||||||
|
|
||||||
|
For APIs that require different CORS configurations for specific routes or requests, you can dynamically generate CORS options based on the incoming request. The `cors` middleware allows you to achieve this by passing a function instead of static options. This function is called for each incoming request and must use the callback pattern to return the appropriate CORS options.
|
||||||
|
|
||||||
|
The function accepts:
|
||||||
|
1. **`req`**:
|
||||||
|
- The incoming request object.
|
||||||
|
|
||||||
|
2. **`callback(error, corsOptions)`**:
|
||||||
|
- A function used to return the computed CORS options.
|
||||||
|
- **Arguments**:
|
||||||
|
- **`error`**: Pass `null` if there’s no error, or an error object to indicate a failure.
|
||||||
|
- **`corsOptions`**: An object specifying the CORS policy for the current request.
|
||||||
|
|
||||||
|
Here’s an example that handles both public routes and restricted, credential-sensitive routes:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var dynamicCorsOptions = function(req, callback) {
|
||||||
|
var corsOptions;
|
||||||
|
if (req.path.startsWith('/auth/connect/')) {
|
||||||
|
// Access-Control-Allow-Origin: http://mydomain.com, Access-Control-Allow-Credentials: true, Vary: Origin
|
||||||
|
corsOptions = {
|
||||||
|
origin: 'http://mydomain.com',
|
||||||
|
credentials: true
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Access-Control-Allow-Origin: *
|
||||||
|
corsOptions = { origin: '*' };
|
||||||
|
}
|
||||||
|
callback(null, corsOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
app.use(cors(dynamicCorsOptions));
|
||||||
|
|
||||||
|
app.get('/auth/connect/twitter', function (req, res) {
|
||||||
|
res.send('Hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/public', function (req, res) {
|
||||||
|
res.send('Hello');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(80, function () {
|
||||||
|
console.log('web server listening on port 80')
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. Possible values:
|
||||||
|
- `Boolean` - set `origin` to `true` to reflect the [request origin](https://datatracker.ietf.org/doc/html/draft-abarth-origin-09), as defined by `req.header('Origin')`, or set it to `false` to disable CORS.
|
||||||
|
- `String` - set `origin` to a specific origin. For example, if you set it to
|
||||||
|
- `"http://example.com"` only requests from "http://example.com" will be allowed.
|
||||||
|
- `"*"` for all domains to be allowed.
|
||||||
|
- `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
|
||||||
|
- `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
|
||||||
|
- `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second.
|
||||||
|
* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
|
||||||
|
* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.
|
||||||
|
* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range') or an array (ex: `['Content-Range', 'X-Content-Range']`). If not specified, no custom headers are exposed.
|
||||||
|
* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header, otherwise it is omitted.
|
||||||
|
* `maxAge`: Configures the **Access-Control-Max-Age** CORS header. Set to an integer to pass the header, otherwise it is omitted.
|
||||||
|
* `preflightContinue`: Pass the CORS preflight response to the next handler.
|
||||||
|
* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
|
||||||
|
|
||||||
|
The default configuration is the equivalent of:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"origin": "*",
|
||||||
|
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||||
|
"preflightContinue": false,
|
||||||
|
"optionsSuccessStatus": 204
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Misconceptions
|
||||||
|
|
||||||
|
### "CORS blocks requests from disallowed origins"
|
||||||
|
|
||||||
|
**No.** Your server receives and processes every request. CORS headers tell the browser whether JavaScript can read the response—not whether the request is allowed.
|
||||||
|
|
||||||
|
### "CORS protects my API from unauthorized access"
|
||||||
|
|
||||||
|
**No.** CORS is not access control. Any HTTP client (curl, Postman, another server) can call your API regardless of CORS settings. Use authentication and authorization to protect your API.
|
||||||
|
|
||||||
|
### "Setting `origin: 'http://example.com'` means only that domain can access my server"
|
||||||
|
|
||||||
|
**No.** It means browsers will only let JavaScript from that origin read responses. The server still responds to all requests.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT License](http://www.opensource.org/licenses/mit-license.php)
|
||||||
|
|
||||||
|
## Original Author
|
||||||
|
|
||||||
|
[Troy Goode](https://github.com/TroyGoode) ([troygoode@gmail.com](mailto:troygoode@gmail.com))
|
||||||
|
|
||||||
|
[coveralls-image]: https://img.shields.io/coveralls/expressjs/cors/master.svg
|
||||||
|
[coveralls-url]: https://coveralls.io/r/expressjs/cors?branch=master
|
||||||
|
[downloads-image]: https://img.shields.io/npm/dm/cors.svg
|
||||||
|
[downloads-url]: https://npmjs.com/package/cors
|
||||||
|
[github-actions-ci-image]: https://img.shields.io/github/actions/workflow/status/expressjs/cors/ci.yml?branch=master&label=ci
|
||||||
|
[github-actions-ci-url]: https://github.com/expressjs/cors?query=workflow%3Aci
|
||||||
|
[npm-image]: https://img.shields.io/npm/v/cors.svg
|
||||||
|
[npm-url]: https://npmjs.com/package/cors
|
||||||
+238
@@ -0,0 +1,238 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var assign = require('object-assign');
|
||||||
|
var vary = require('vary');
|
||||||
|
|
||||||
|
var defaults = {
|
||||||
|
origin: '*',
|
||||||
|
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||||
|
preflightContinue: false,
|
||||||
|
optionsSuccessStatus: 204
|
||||||
|
};
|
||||||
|
|
||||||
|
function isString(s) {
|
||||||
|
return typeof s === 'string' || s instanceof String;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOriginAllowed(origin, allowedOrigin) {
|
||||||
|
if (Array.isArray(allowedOrigin)) {
|
||||||
|
for (var i = 0; i < allowedOrigin.length; ++i) {
|
||||||
|
if (isOriginAllowed(origin, allowedOrigin[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else if (isString(allowedOrigin)) {
|
||||||
|
return origin === allowedOrigin;
|
||||||
|
} else if (allowedOrigin instanceof RegExp) {
|
||||||
|
return allowedOrigin.test(origin);
|
||||||
|
} else {
|
||||||
|
return !!allowedOrigin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureOrigin(options, req) {
|
||||||
|
var requestOrigin = req.headers.origin,
|
||||||
|
headers = [],
|
||||||
|
isAllowed;
|
||||||
|
|
||||||
|
if (!options.origin || options.origin === '*') {
|
||||||
|
// allow any origin
|
||||||
|
headers.push([{
|
||||||
|
key: 'Access-Control-Allow-Origin',
|
||||||
|
value: '*'
|
||||||
|
}]);
|
||||||
|
} else if (isString(options.origin)) {
|
||||||
|
// fixed origin
|
||||||
|
headers.push([{
|
||||||
|
key: 'Access-Control-Allow-Origin',
|
||||||
|
value: options.origin
|
||||||
|
}]);
|
||||||
|
headers.push([{
|
||||||
|
key: 'Vary',
|
||||||
|
value: 'Origin'
|
||||||
|
}]);
|
||||||
|
} else {
|
||||||
|
isAllowed = isOriginAllowed(requestOrigin, options.origin);
|
||||||
|
// reflect origin
|
||||||
|
headers.push([{
|
||||||
|
key: 'Access-Control-Allow-Origin',
|
||||||
|
value: isAllowed ? requestOrigin : false
|
||||||
|
}]);
|
||||||
|
headers.push([{
|
||||||
|
key: 'Vary',
|
||||||
|
value: 'Origin'
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureMethods(options) {
|
||||||
|
var methods = options.methods;
|
||||||
|
if (methods.join) {
|
||||||
|
methods = options.methods.join(','); // .methods is an array, so turn it into a string
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
key: 'Access-Control-Allow-Methods',
|
||||||
|
value: methods
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureCredentials(options) {
|
||||||
|
if (options.credentials === true) {
|
||||||
|
return {
|
||||||
|
key: 'Access-Control-Allow-Credentials',
|
||||||
|
value: 'true'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureAllowedHeaders(options, req) {
|
||||||
|
var allowedHeaders = options.allowedHeaders || options.headers;
|
||||||
|
var headers = [];
|
||||||
|
|
||||||
|
if (!allowedHeaders) {
|
||||||
|
allowedHeaders = req.headers['access-control-request-headers']; // .headers wasn't specified, so reflect the request headers
|
||||||
|
headers.push([{
|
||||||
|
key: 'Vary',
|
||||||
|
value: 'Access-Control-Request-Headers'
|
||||||
|
}]);
|
||||||
|
} else if (allowedHeaders.join) {
|
||||||
|
allowedHeaders = allowedHeaders.join(','); // .headers is an array, so turn it into a string
|
||||||
|
}
|
||||||
|
if (allowedHeaders && allowedHeaders.length) {
|
||||||
|
headers.push([{
|
||||||
|
key: 'Access-Control-Allow-Headers',
|
||||||
|
value: allowedHeaders
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureExposedHeaders(options) {
|
||||||
|
var headers = options.exposedHeaders;
|
||||||
|
if (!headers) {
|
||||||
|
return null;
|
||||||
|
} else if (headers.join) {
|
||||||
|
headers = headers.join(','); // .headers is an array, so turn it into a string
|
||||||
|
}
|
||||||
|
if (headers && headers.length) {
|
||||||
|
return {
|
||||||
|
key: 'Access-Control-Expose-Headers',
|
||||||
|
value: headers
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function configureMaxAge(options) {
|
||||||
|
var maxAge = (typeof options.maxAge === 'number' || options.maxAge) && options.maxAge.toString()
|
||||||
|
if (maxAge && maxAge.length) {
|
||||||
|
return {
|
||||||
|
key: 'Access-Control-Max-Age',
|
||||||
|
value: maxAge
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyHeaders(headers, res) {
|
||||||
|
for (var i = 0, n = headers.length; i < n; i++) {
|
||||||
|
var header = headers[i];
|
||||||
|
if (header) {
|
||||||
|
if (Array.isArray(header)) {
|
||||||
|
applyHeaders(header, res);
|
||||||
|
} else if (header.key === 'Vary' && header.value) {
|
||||||
|
vary(res, header.value);
|
||||||
|
} else if (header.value) {
|
||||||
|
res.setHeader(header.key, header.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cors(options, req, res, next) {
|
||||||
|
var headers = [],
|
||||||
|
method = req.method && req.method.toUpperCase && req.method.toUpperCase();
|
||||||
|
|
||||||
|
if (method === 'OPTIONS') {
|
||||||
|
// preflight
|
||||||
|
headers.push(configureOrigin(options, req));
|
||||||
|
headers.push(configureCredentials(options))
|
||||||
|
headers.push(configureMethods(options))
|
||||||
|
headers.push(configureAllowedHeaders(options, req));
|
||||||
|
headers.push(configureMaxAge(options))
|
||||||
|
headers.push(configureExposedHeaders(options))
|
||||||
|
applyHeaders(headers, res);
|
||||||
|
|
||||||
|
if (options.preflightContinue) {
|
||||||
|
next();
|
||||||
|
} else {
|
||||||
|
// Safari (and potentially other browsers) need content-length 0,
|
||||||
|
// for 204 or they just hang waiting for a body
|
||||||
|
res.statusCode = options.optionsSuccessStatus;
|
||||||
|
res.setHeader('Content-Length', '0');
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// actual response
|
||||||
|
headers.push(configureOrigin(options, req));
|
||||||
|
headers.push(configureCredentials(options))
|
||||||
|
headers.push(configureExposedHeaders(options))
|
||||||
|
applyHeaders(headers, res);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function middlewareWrapper(o) {
|
||||||
|
// if options are static (either via defaults or custom options passed in), wrap in a function
|
||||||
|
var optionsCallback = null;
|
||||||
|
if (typeof o === 'function') {
|
||||||
|
optionsCallback = o;
|
||||||
|
} else {
|
||||||
|
optionsCallback = function (req, cb) {
|
||||||
|
cb(null, o);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return function corsMiddleware(req, res, next) {
|
||||||
|
optionsCallback(req, function (err, options) {
|
||||||
|
if (err) {
|
||||||
|
next(err);
|
||||||
|
} else {
|
||||||
|
var corsOptions = assign({}, defaults, options);
|
||||||
|
var originCallback = null;
|
||||||
|
if (corsOptions.origin && typeof corsOptions.origin === 'function') {
|
||||||
|
originCallback = corsOptions.origin;
|
||||||
|
} else if (corsOptions.origin) {
|
||||||
|
originCallback = function (origin, cb) {
|
||||||
|
cb(null, corsOptions.origin);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (originCallback) {
|
||||||
|
originCallback(req.headers.origin, function (err2, origin) {
|
||||||
|
if (err2 || !origin) {
|
||||||
|
next(err2);
|
||||||
|
} else {
|
||||||
|
corsOptions.origin = origin;
|
||||||
|
cors(corsOptions, req, res, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// can pass either an options hash, an options delegate, or nothing
|
||||||
|
module.exports = middlewareWrapper;
|
||||||
|
|
||||||
|
}());
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "cors",
|
||||||
|
"description": "Node.js CORS middleware",
|
||||||
|
"version": "2.8.6",
|
||||||
|
"author": "Troy Goode <troygoode@gmail.com> (https://github.com/troygoode/)",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"cors",
|
||||||
|
"express",
|
||||||
|
"connect",
|
||||||
|
"middleware"
|
||||||
|
],
|
||||||
|
"repository": "expressjs/cors",
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"after": "0.8.2",
|
||||||
|
"eslint": "7.30.0",
|
||||||
|
"express": "4.21.2",
|
||||||
|
"mocha": "9.2.2",
|
||||||
|
"nyc": "15.1.0",
|
||||||
|
"supertest": "6.1.3"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/index.js"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "npm run lint && npm run test-ci",
|
||||||
|
"test-ci": "nyc --reporter=lcov --reporter=text mocha --require test/support/env",
|
||||||
|
"lint": "eslint lib test"
|
||||||
|
}
|
||||||
|
}
|
||||||
+90
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
object-assign
|
||||||
|
(c) Sindre Sorhus
|
||||||
|
@license MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
/* eslint-disable no-unused-vars */
|
||||||
|
var getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
||||||
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||||
|
var propIsEnumerable = Object.prototype.propertyIsEnumerable;
|
||||||
|
|
||||||
|
function toObject(val) {
|
||||||
|
if (val === null || val === undefined) {
|
||||||
|
throw new TypeError('Object.assign cannot be called with null or undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldUseNative() {
|
||||||
|
try {
|
||||||
|
if (!Object.assign) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect buggy property enumeration order in older V8 versions.
|
||||||
|
|
||||||
|
// https://bugs.chromium.org/p/v8/issues/detail?id=4118
|
||||||
|
var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
|
||||||
|
test1[5] = 'de';
|
||||||
|
if (Object.getOwnPropertyNames(test1)[0] === '5') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
|
||||||
|
var test2 = {};
|
||||||
|
for (var i = 0; i < 10; i++) {
|
||||||
|
test2['_' + String.fromCharCode(i)] = i;
|
||||||
|
}
|
||||||
|
var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
|
||||||
|
return test2[n];
|
||||||
|
});
|
||||||
|
if (order2.join('') !== '0123456789') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://bugs.chromium.org/p/v8/issues/detail?id=3056
|
||||||
|
var test3 = {};
|
||||||
|
'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
|
||||||
|
test3[letter] = letter;
|
||||||
|
});
|
||||||
|
if (Object.keys(Object.assign({}, test3)).join('') !==
|
||||||
|
'abcdefghijklmnopqrst') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
// We don't expect any of the above to throw, but better to be safe.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = shouldUseNative() ? Object.assign : function (target, source) {
|
||||||
|
var from;
|
||||||
|
var to = toObject(target);
|
||||||
|
var symbols;
|
||||||
|
|
||||||
|
for (var s = 1; s < arguments.length; s++) {
|
||||||
|
from = Object(arguments[s]);
|
||||||
|
|
||||||
|
for (var key in from) {
|
||||||
|
if (hasOwnProperty.call(from, key)) {
|
||||||
|
to[key] = from[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getOwnPropertySymbols) {
|
||||||
|
symbols = getOwnPropertySymbols(from);
|
||||||
|
for (var i = 0; i < symbols.length; i++) {
|
||||||
|
if (propIsEnumerable.call(from, symbols[i])) {
|
||||||
|
to[symbols[i]] = from[symbols[i]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return to;
|
||||||
|
};
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"name": "object-assign",
|
||||||
|
"version": "4.1.1",
|
||||||
|
"description": "ES2015 `Object.assign()` ponyfill",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "sindresorhus/object-assign",
|
||||||
|
"author": {
|
||||||
|
"name": "Sindre Sorhus",
|
||||||
|
"email": "sindresorhus@gmail.com",
|
||||||
|
"url": "sindresorhus.com"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "xo && ava",
|
||||||
|
"bench": "matcha bench.js"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"index.js"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"object",
|
||||||
|
"assign",
|
||||||
|
"extend",
|
||||||
|
"properties",
|
||||||
|
"es2015",
|
||||||
|
"ecmascript",
|
||||||
|
"harmony",
|
||||||
|
"ponyfill",
|
||||||
|
"prollyfill",
|
||||||
|
"polyfill",
|
||||||
|
"shim",
|
||||||
|
"browser"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"ava": "^0.16.0",
|
||||||
|
"lodash": "^4.16.4",
|
||||||
|
"matcha": "^0.7.0",
|
||||||
|
"xo": "^0.16.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
# object-assign [](https://travis-ci.org/sindresorhus/object-assign)
|
||||||
|
|
||||||
|
> ES2015 [`Object.assign()`](http://www.2ality.com/2014/01/object-assign.html) [ponyfill](https://ponyfill.com)
|
||||||
|
|
||||||
|
|
||||||
|
## Use the built-in
|
||||||
|
|
||||||
|
Node.js 4 and up, as well as every evergreen browser (Chrome, Edge, Firefox, Opera, Safari),
|
||||||
|
support `Object.assign()` :tada:. If you target only those environments, then by all
|
||||||
|
means, use `Object.assign()` instead of this package.
|
||||||
|
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```
|
||||||
|
$ npm install --save object-assign
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
const objectAssign = require('object-assign');
|
||||||
|
|
||||||
|
objectAssign({foo: 0}, {bar: 1});
|
||||||
|
//=> {foo: 0, bar: 1}
|
||||||
|
|
||||||
|
// multiple sources
|
||||||
|
objectAssign({foo: 0}, {bar: 1}, {baz: 2});
|
||||||
|
//=> {foo: 0, bar: 1, baz: 2}
|
||||||
|
|
||||||
|
// overwrites equal keys
|
||||||
|
objectAssign({foo: 0}, {foo: 1}, {foo: 2});
|
||||||
|
//=> {foo: 2}
|
||||||
|
|
||||||
|
// ignores null and undefined sources
|
||||||
|
objectAssign({foo: 0}, null, {bar: 1}, undefined);
|
||||||
|
//=> {foo: 0, bar: 1}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
### objectAssign(target, [source, ...])
|
||||||
|
|
||||||
|
Assigns enumerable own properties of `source` objects to the `target` object and returns the `target` object. Additional `source` objects will overwrite previous ones.
|
||||||
|
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
- [ES2015 spec - Object.assign](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.assign)
|
||||||
|
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [deep-assign](https://github.com/sindresorhus/deep-assign) - Recursive `Object.assign()`
|
||||||
|
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT © [Sindre Sorhus](https://sindresorhus.com)
|
||||||
Generated
+27
@@ -10,6 +10,7 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
|
"cors": "^2.8.6",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"mysql2": "^3.19.1"
|
"mysql2": "^3.19.1"
|
||||||
@@ -178,6 +179,23 @@
|
|||||||
"node": ">=6.6.0"
|
"node": ">=6.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cors": {
|
||||||
|
"version": "2.8.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
|
||||||
|
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"object-assign": "^4",
|
||||||
|
"vary": "^1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.4.3",
|
"version": "4.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
||||||
@@ -756,6 +774,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-assign": {
|
||||||
|
"version": "4.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-inspect": {
|
"node_modules/object-inspect": {
|
||||||
"version": "1.13.4",
|
"version": "1.13.4",
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"homepage": "https://github.com/Syco21/YuGiOh-Database-API#readme",
|
"homepage": "https://github.com/Syco21/YuGiOh-Database-API#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
|
"cors": "^2.8.6",
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"mysql2": "^3.19.1"
|
"mysql2": "^3.19.1"
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
const db = require('../config/db');
|
||||||
|
const axios = require('axios');
|
||||||
|
|
||||||
|
// Use your existing insertCardImage for initial URL insert
|
||||||
|
const { insertCardImage } = require('../models/cardImageModel');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /cardImage/:cardId
|
||||||
|
* Returns the image blob of a card. If not stored, downloads from image_url, saves to DB.
|
||||||
|
*/
|
||||||
|
async function getCardImage(req, res) {
|
||||||
|
const { cardId } = req.params;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if blob exists
|
||||||
|
const [rows] = await db.execute(
|
||||||
|
'SELECT image_data, image_url FROM card_images WHERE card_id = ? LIMIT 1',
|
||||||
|
[cardId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (rows.length === 0) {
|
||||||
|
return res.status(404).json({ error: 'Card image not found' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const cardImage = rows[0];
|
||||||
|
|
||||||
|
if (cardImage.image_data) {
|
||||||
|
// Blob exists → return as base64
|
||||||
|
const base64Image = cardImage.image_data.toString('base64');
|
||||||
|
return res.json({ image: `data:image/png;base64,${base64Image}` });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blob does not exist → download from URL
|
||||||
|
if (!cardImage.image_url) {
|
||||||
|
return res.status(404).json({ error: 'No image URL available for this card' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await axios.get(cardImage.image_url, {
|
||||||
|
responseType: 'arraybuffer'
|
||||||
|
});
|
||||||
|
|
||||||
|
const imageBuffer = Buffer.from(response.data, 'binary');
|
||||||
|
|
||||||
|
// Save blob to DB
|
||||||
|
await db.execute(
|
||||||
|
'UPDATE card_images SET image_data = ? WHERE card_id = ?',
|
||||||
|
[imageBuffer, cardId]
|
||||||
|
);
|
||||||
|
|
||||||
|
const base64Image = imageBuffer.toString('base64');
|
||||||
|
res.json({ image: `data:image/png;base64,${base64Image}` });
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching card image:', err);
|
||||||
|
res.status(500).json({ error: 'Failed to fetch card image' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { getCardImage };
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
const db = require('../config/db');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /exportCards
|
||||||
|
* Returns all cards with their printings and amount_owned
|
||||||
|
*/
|
||||||
|
async function exportCards(req, res) {
|
||||||
|
try {
|
||||||
|
// Fetch all cards
|
||||||
|
const [cards] = await db.execute(`
|
||||||
|
SELECT id, name, card_type AS type, frame_type, level, race, attribute, link_val, tcg_date, ocg_date
|
||||||
|
FROM cards
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Fetch all printings with sets and rarities
|
||||||
|
const [printings] = await db.execute(`
|
||||||
|
SELECT csr.card_id, csr.set_id, csr.card_set_code AS set_code, csr.amount_owned,
|
||||||
|
s.set_name,
|
||||||
|
r.id AS rarity_id, r.rarity_name, r.rarity_code
|
||||||
|
FROM card_sets_rarity csr
|
||||||
|
JOIN sets s ON csr.set_id = s.id
|
||||||
|
JOIN rarities r ON csr.rarity_id = r.id
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Map printings to cards
|
||||||
|
const cardMap = {};
|
||||||
|
cards.forEach(card => {
|
||||||
|
cardMap[card.id] = { ...card, printings: [] };
|
||||||
|
});
|
||||||
|
|
||||||
|
printings.forEach(p => {
|
||||||
|
if (cardMap[p.card_id]) {
|
||||||
|
cardMap[p.card_id].printings.push({
|
||||||
|
set_id: p.set_id,
|
||||||
|
set_name: p.set_name,
|
||||||
|
set_code: p.set_code,
|
||||||
|
rarity_id: p.rarity_id,
|
||||||
|
rarity_name: p.rarity_name,
|
||||||
|
rarity_code: p.rarity_code,
|
||||||
|
amount_owned: p.amount_owned || 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
res.json(Object.values(cardMap));
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error exporting cards:', err);
|
||||||
|
res.status(500).json({ error: 'Failed to export cards' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { exportCards };
|
||||||
+11
-3
@@ -1,16 +1,24 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
// Routes
|
||||||
const importRoutes = require('./routes/importRoutes');
|
const importRoutes = require('./routes/importRoutes');
|
||||||
|
const collectionRoutes = require('./routes/collectionRoutes');
|
||||||
|
const exportCardsRoutes = require('./routes/exportCardsRoutes');
|
||||||
|
const cardImageRoutes = require('./routes/cardImageRoutes'); // <-- new
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
app.use(cors()); // enable CORS for frontend
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Mount routes
|
||||||
app.use('/import', importRoutes);
|
app.use('/import', importRoutes);
|
||||||
|
app.use('/collection', collectionRoutes);
|
||||||
|
app.use('/exportCards', exportCardsRoutes);
|
||||||
|
app.use('/cardImage', cardImageRoutes); // <-- mount the new card image route
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
const PORT = process.env.PORT || 3000;
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`Server running on http://localhost:${PORT}`);
|
console.log(`Server running on http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
const collectionRoutes = require('./routes/collectionRoutes');
|
|
||||||
app.use('/collection', collectionRoutes);
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { getCardImage } = require('../controllers/cardImageController');
|
||||||
|
|
||||||
|
// GET /cardImage/:cardId
|
||||||
|
router.get('/:cardId', getCardImage);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
const { exportCards } = require('../controllers/exportCardsController');
|
||||||
|
|
||||||
|
// GET /exportCards
|
||||||
|
router.get('/', exportCards);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
Reference in New Issue
Block a user