mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-18 00:55:54 +00:00
Remove deprecated endpoints
This commit is contained in:
parent
71f754f9f9
commit
2a055064a1
6 changed files with 1 additions and 494 deletions
|
|
@ -4,7 +4,6 @@ import request from "supertest"; // supertest is a framework that allows to easi
|
|||
import { createApp } from "../app";
|
||||
import SkinModel from "../../data/SkinModel";
|
||||
import * as S3 from "../../s3";
|
||||
import * as Auth from "../auth";
|
||||
import { processUserUploads } from "../processUserUploads";
|
||||
import UserContext from "../../data/UserContext";
|
||||
import { searchIndex } from "../../algolia";
|
||||
|
|
@ -106,37 +105,6 @@ describe(".me", () => {
|
|||
});
|
||||
});
|
||||
|
||||
// TODO: The redirect_uri is different on github
|
||||
test("/auth", async () => {
|
||||
const { body } = await request(app).get("/auth").expect(302);
|
||||
// TODO: The redirect_uri is different on github
|
||||
// .expect(
|
||||
// "Location",
|
||||
// "https://discord.com/api/oauth2/authorize?client_id=%3CDUMMY_DISCORD_CLIENT_ID%3E&redirect_uri=https%3A%2F%2Fapi.webampskins.org%2Fauth%2Fdiscord&response_type=code&scope=identify%20guilds"
|
||||
// );
|
||||
expect(body).toEqual({});
|
||||
});
|
||||
|
||||
describe("/auth/discord", () => {
|
||||
test("valid code", async () => {
|
||||
const response = await request(app)
|
||||
.get("/auth/discord")
|
||||
.query({ code: "<A_FAKE_CODE>" })
|
||||
.expect(302);
|
||||
// TODO: The location is different on github
|
||||
// .expect("Location", "https://skins.webamp.org/review/");
|
||||
// TODO: Assert that we get cookie headers. I think that will not work now
|
||||
// because express does not think it's secure in a test env.
|
||||
expect(Auth.auth).toHaveBeenCalledWith("<A_FAKE_CODE>");
|
||||
expect(response.body).toEqual({});
|
||||
});
|
||||
test("missing code", async () => {
|
||||
const { body } = await request(app).get("/auth/discord").expect(400);
|
||||
expect(Auth.auth).not.toHaveBeenCalled();
|
||||
expect(body).toEqual({ message: "Expected to get a code" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("Query.skins", () => {
|
||||
test("no query params", async () => {
|
||||
const { data } = await graphQLRequest(
|
||||
|
|
|
|||
|
|
@ -1,381 +0,0 @@
|
|||
import { Application } from "express";
|
||||
import { knex } from "../../db";
|
||||
import request from "supertest"; // supertest is a framework that allows to easily test web apis
|
||||
import { createApp } from "../app";
|
||||
import SkinModel from "../../data/SkinModel";
|
||||
import * as S3 from "../../s3";
|
||||
import * as Auth from "../auth";
|
||||
import { processUserUploads } from "../processUserUploads";
|
||||
import UserContext from "../../data/UserContext";
|
||||
import { searchIndex } from "../../algolia";
|
||||
jest.mock("../../s3");
|
||||
jest.mock("../../algolia");
|
||||
jest.mock("../processUserUploads");
|
||||
jest.mock("../auth");
|
||||
|
||||
let app: Application;
|
||||
const handler = jest.fn();
|
||||
const log = jest.fn();
|
||||
const logError = jest.fn();
|
||||
|
||||
let username: string | undefined;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
username = "<MOCKED>";
|
||||
app = createApp({
|
||||
eventHandler: handler,
|
||||
extraMiddleware: (req, res, next) => {
|
||||
req.session.username = username;
|
||||
next();
|
||||
},
|
||||
logger: { log, logError },
|
||||
});
|
||||
await knex.migrate.latest();
|
||||
await knex.seed.run();
|
||||
});
|
||||
|
||||
describe("/authed", () => {
|
||||
test("logged in ", async () => {
|
||||
const { body } = await request(app).get("/authed").expect(200);
|
||||
expect(body).toEqual({ username: "<MOCKED>" });
|
||||
});
|
||||
test("not logged in", async () => {
|
||||
username = undefined;
|
||||
const { body } = await request(app).get("/authed").expect(200);
|
||||
expect(body).toEqual({ username: null });
|
||||
});
|
||||
});
|
||||
|
||||
test.skip("/auth", async () => {
|
||||
const { body } = await request(app)
|
||||
.get("/auth")
|
||||
.expect(302)
|
||||
.expect(
|
||||
"Location",
|
||||
"https://discord.com/api/oauth2/authorize?client_id=%3CDUMMY_DISCORD_CLIENT_ID%3E&redirect_uri=https%3A%2F%2Fapi.webampskins.org%2Fauth%2Fdiscord&response_type=code&scope=identify%20guilds"
|
||||
);
|
||||
expect(body).toEqual({});
|
||||
});
|
||||
|
||||
describe.skip("/auth/discord", () => {
|
||||
test("valid code", async () => {
|
||||
const response = await request(app)
|
||||
.get("/auth/discord")
|
||||
.query({ code: "<A_FAKE_CODE>" })
|
||||
.expect(302)
|
||||
.expect("Location", "https://skins.webamp.org/review/");
|
||||
// TODO: Assert that we get cookie headers. I think that will not work now
|
||||
// because express does not think it's secure in a test env.
|
||||
expect(Auth.auth).toHaveBeenCalledWith("<A_FAKE_CODE>");
|
||||
expect(response.body).toEqual({});
|
||||
});
|
||||
test("missing code", async () => {
|
||||
const { body } = await request(app).get("/auth/discord").expect(400);
|
||||
expect(Auth.auth).not.toHaveBeenCalled();
|
||||
expect(body).toEqual({ message: "Expected to get a code" });
|
||||
});
|
||||
});
|
||||
|
||||
describe("/skins/", () => {
|
||||
test("no query params", async () => {
|
||||
const { body } = await request(app).get("/skins/");
|
||||
expect(body).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"skinCount": 6,
|
||||
"skins": Array [
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "Zelda_Amp_3.wsz",
|
||||
"md5": "48bbdbbeb03d347e59b1eebda4d352d0",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "path.wsz",
|
||||
"md5": "a_fake_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "rejected.wsz",
|
||||
"md5": "a_rejected_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "nsfw.wsz",
|
||||
"md5": "a_nsfw_md5",
|
||||
"nsfw": true,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
test("first and offset", async () => {
|
||||
const { body } = await request(app)
|
||||
.get("/skins/")
|
||||
.query({ first: 2, offset: 1 });
|
||||
expect(body).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"skinCount": 6,
|
||||
"skins": Array [
|
||||
Object {
|
||||
"fileName": "Zelda_Amp_3.wsz",
|
||||
"md5": "48bbdbbeb03d347e59b1eebda4d352d0",
|
||||
"nsfw": false,
|
||||
},
|
||||
Object {
|
||||
"fileName": "path.wsz",
|
||||
"md5": "a_fake_md5",
|
||||
"nsfw": false,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
// This is deprecated and fails in CI due to printing as localize date string.
|
||||
test.skip("/skins/a_fake_md5/debug", async () => {
|
||||
const { body } = await request(app)
|
||||
.get("/skins/a_fake_md5/debug")
|
||||
.expect(200);
|
||||
expect(body).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("/skins/a_fake_md5/report", async () => {
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_fake_md5/report")
|
||||
.expect(200);
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
type: "REVIEW_REQUESTED",
|
||||
md5: "a_fake_md5",
|
||||
});
|
||||
expect(body).toEqual({}); // TODO: Where does the text response go?
|
||||
});
|
||||
|
||||
test("/skins/a_fake_md5/approve", async () => {
|
||||
const ctx = new UserContext();
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_fake_md5/approve")
|
||||
.expect(200);
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
type: "APPROVED_SKIN",
|
||||
md5: "a_fake_md5",
|
||||
});
|
||||
expect(body).toEqual({ message: "The skin has been approved." });
|
||||
const skin = await SkinModel.fromMd5(ctx, "a_fake_md5");
|
||||
|
||||
expect(await skin?.getTweetStatus()).toEqual("APPROVED");
|
||||
});
|
||||
|
||||
describe("/to_review", () => {
|
||||
test("logged in ", async () => {
|
||||
const { body } = await request(app).get("/to_review").expect(200);
|
||||
|
||||
expect(body).toEqual({
|
||||
filename: expect.any(String),
|
||||
md5: expect.any(String),
|
||||
});
|
||||
});
|
||||
test("not logged in ", async () => {
|
||||
username = undefined;
|
||||
const { body } = await request(app).get("/to_review").expect(403);
|
||||
expect(body).toEqual({ message: "You must be logged in" });
|
||||
});
|
||||
});
|
||||
|
||||
test("/skins/a_md5_that_does_not_exist/approve (404)", async () => {
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_md5_that_does_not_exist/approve")
|
||||
.expect(404);
|
||||
|
||||
expect(body).toEqual({});
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("/skins/a_fake_md5/reject", async () => {
|
||||
const ctx = new UserContext();
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_fake_md5/reject")
|
||||
.expect(200);
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
type: "REJECTED_SKIN",
|
||||
md5: "a_fake_md5",
|
||||
});
|
||||
expect(body).toEqual({ message: "The skin has been rejected." }); // TODO: Where does the text response go?
|
||||
const skin = await SkinModel.fromMd5(ctx, "a_fake_md5");
|
||||
|
||||
expect(await skin?.getTweetStatus()).toEqual("REJECTED");
|
||||
});
|
||||
|
||||
test("/skins/a_md5_that_does_not_exist/reject (404)", async () => {
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_md5_that_does_not_exist/reject")
|
||||
.expect(404);
|
||||
|
||||
expect(body).toEqual({});
|
||||
expect(handler).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("/skins/a_fake_md5/nsfw", async () => {
|
||||
const ctx = new UserContext();
|
||||
const { body } = await request(app)
|
||||
.post("/skins/a_fake_md5/nsfw")
|
||||
.expect(200);
|
||||
expect(handler).toHaveBeenCalledWith({
|
||||
type: "MARKED_SKIN_NSFW",
|
||||
md5: "a_fake_md5",
|
||||
});
|
||||
expect(searchIndex.partialUpdateObjects).toHaveBeenCalledWith([
|
||||
{ nsfw: true, objectID: "a_fake_md5" },
|
||||
]);
|
||||
expect(body).toEqual({ message: "The skin has been marked as NSFW." });
|
||||
const skin = await SkinModel.fromMd5(ctx, "a_fake_md5");
|
||||
|
||||
expect(await skin?.getTweetStatus()).toEqual("NSFW");
|
||||
});
|
||||
|
||||
// TODO: Actually upload some skins?
|
||||
test("/skins/status", async () => {
|
||||
const { body } = await request(app)
|
||||
.post("/skins/status")
|
||||
.send({ hashes: ["a_fake_md5", "a_missing_md5"] });
|
||||
expect(body).toEqual({});
|
||||
});
|
||||
|
||||
test("/approved", async () => {
|
||||
const { body } = await request(app).get("/approved").expect(200);
|
||||
expect(body).toEqual(["an_approved_md5", "a_tweeted_md5"]);
|
||||
});
|
||||
|
||||
test("/skins/a_fake_md5", async () => {
|
||||
let response = await request(app).get("/skins/a_fake_md5");
|
||||
expect(response.body).toEqual({
|
||||
fileName: "path.wsz",
|
||||
md5: "a_fake_md5",
|
||||
nsfw: false,
|
||||
});
|
||||
response = await request(app).get("/skins/a_nsfw_md5");
|
||||
expect(response.body).toEqual({
|
||||
fileName: "nsfw.wsz",
|
||||
md5: "a_nsfw_md5",
|
||||
nsfw: true,
|
||||
});
|
||||
await request(app).get("/skins/does_not_exist_md5").expect(404);
|
||||
});
|
||||
|
||||
test("/skins/get_upload_urls", async () => {
|
||||
const { body } = await request(app)
|
||||
.post("/skins/get_upload_urls")
|
||||
.send({
|
||||
skins: {
|
||||
"3b73bcd43c30b85d4cad3083e8ac9695": "a_fake_new_file.wsz",
|
||||
"48bbdbbeb03d347e59b1eebda4d352d0":
|
||||
"a_new_name_for_a_file_that_exists.wsz",
|
||||
},
|
||||
});
|
||||
|
||||
expect(S3.getSkinUploadUrl).toHaveBeenCalledWith(
|
||||
"3b73bcd43c30b85d4cad3083e8ac9695",
|
||||
expect.any(Number)
|
||||
);
|
||||
|
||||
expect(body).toEqual({
|
||||
"3b73bcd43c30b85d4cad3083e8ac9695": {
|
||||
id: expect.any(Number),
|
||||
url: "<MOCK_S3_UPLOAD_URL>",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("An Upload Flow", async () => {
|
||||
// Request an upload URL
|
||||
const md5 = "3b73bcd43c30b85d4cad3083e8ac9695";
|
||||
const filename = "a_fake_new_file.wsz";
|
||||
const skins = { [md5]: filename };
|
||||
const getUrlsResponse = await request(app)
|
||||
.post("/skins/get_upload_urls")
|
||||
.send({ skins });
|
||||
|
||||
const id = getUrlsResponse.body[md5].id;
|
||||
|
||||
expect(getUrlsResponse.body).toEqual({
|
||||
[md5]: { id: expect.any(Number), url: "<MOCK_S3_UPLOAD_URL>" },
|
||||
});
|
||||
|
||||
const requestedUpload = await knex("skin_uploads").where({ id }).first();
|
||||
expect(requestedUpload).toEqual({
|
||||
filename,
|
||||
id,
|
||||
skin_md5: md5,
|
||||
status: "URL_REQUESTED",
|
||||
});
|
||||
|
||||
// Report that we've uploaded the skin to S3 (we lie)
|
||||
const uploadedResponse = await request(app)
|
||||
.post(`/skins/${md5}/uploaded`)
|
||||
.query({ id })
|
||||
.send({ skins });
|
||||
expect(uploadedResponse.body).toEqual({ done: true });
|
||||
expect(processUserUploads).toHaveBeenCalled();
|
||||
|
||||
const reportedUpload = await knex("skin_uploads").where({ id }).first();
|
||||
expect(reportedUpload).toEqual({
|
||||
filename,
|
||||
id,
|
||||
skin_md5: md5,
|
||||
status: "UPLOAD_REPORTED",
|
||||
});
|
||||
});
|
||||
|
||||
test("/stylegan.json", async () => {
|
||||
const response = await request(app).get("/stylegan.json");
|
||||
expect(response.body).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"fileName": "Zelda_Amp_3.wsz",
|
||||
"md5": "48bbdbbeb03d347e59b1eebda4d352d0",
|
||||
"nsfw": false,
|
||||
"url": "https://r2.webampskins.org/screenshots/48bbdbbeb03d347e59b1eebda4d352d0.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "path.wsz",
|
||||
"md5": "a_fake_md5",
|
||||
"nsfw": false,
|
||||
"url": "https://r2.webampskins.org/screenshots/a_fake_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "nsfw.wsz",
|
||||
"md5": "a_nsfw_md5",
|
||||
"nsfw": true,
|
||||
"url": "https://r2.webampskins.org/screenshots/a_nsfw_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "rejected.wsz",
|
||||
"md5": "a_rejected_md5",
|
||||
"nsfw": false,
|
||||
"url": "https://r2.webampskins.org/screenshots/a_rejected_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "tweeted.wsz",
|
||||
"md5": "a_tweeted_md5",
|
||||
"nsfw": false,
|
||||
"url": "https://r2.webampskins.org/screenshots/a_tweeted_md5.png",
|
||||
},
|
||||
Object {
|
||||
"fileName": "approved.wsz",
|
||||
"md5": "an_approved_md5",
|
||||
"nsfw": false,
|
||||
"url": "https://r2.webampskins.org/screenshots/an_approved_md5.png",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import router from "./router";
|
||||
import graphql from "./graphql";
|
||||
import cors, { CorsOptions } from "cors";
|
||||
import bodyParser from "body-parser";
|
||||
import Sentry from "@sentry/node";
|
||||
import expressSitemapXml from "express-sitemap-xml";
|
||||
import * as Skins from "../data/skins";
|
||||
|
|
@ -139,17 +137,10 @@ export function createApp({ eventHandler, extraMiddleware, logger }: Options) {
|
|||
app.use(cors(corsOptions));
|
||||
app.options("*", cors(corsOptions));
|
||||
|
||||
// Configure json output
|
||||
app.set("json spaces", 2);
|
||||
|
||||
// parse application/json
|
||||
app.use(bodyParser.json() as RequestHandler);
|
||||
|
||||
// Configure sitemap
|
||||
app.use(expressSitemapXml(getSitemapUrls, "https://skins.webamp.org"));
|
||||
|
||||
// Add routes
|
||||
app.use("/", router);
|
||||
app.use("/graphql", graphql);
|
||||
|
||||
// The error handler must be before any other error middleware and after all controllers
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
import { Router } from "express";
|
||||
import asyncHandler from "express-async-handler";
|
||||
import {
|
||||
DISCORD_CLIENT_ID,
|
||||
DISCORD_REDIRECT_URL,
|
||||
LOGIN_REDIRECT_URL,
|
||||
} from "../config";
|
||||
import { auth } from "./auth";
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Purposefully REST
|
||||
router.get(
|
||||
"/auth/",
|
||||
asyncHandler(async (req, res) => {
|
||||
res.redirect(
|
||||
302,
|
||||
`https://discord.com/api/oauth2/authorize?client_id=${encodeURIComponent(
|
||||
DISCORD_CLIENT_ID
|
||||
)}&redirect_uri=${encodeURIComponent(
|
||||
DISCORD_REDIRECT_URL
|
||||
)}&response_type=code&scope=identify%20guilds`
|
||||
);
|
||||
})
|
||||
);
|
||||
// Purposefully REST
|
||||
router.get(
|
||||
"/auth/discord",
|
||||
asyncHandler(async (req, res) => {
|
||||
const code = req.query.code as string | undefined;
|
||||
|
||||
if (code == null) {
|
||||
res.status(400).send({ message: "Expected to get a code" });
|
||||
return;
|
||||
}
|
||||
const username = await auth(code);
|
||||
if (username == null) {
|
||||
res.status(400).send({ message: "Invalid code" });
|
||||
return;
|
||||
}
|
||||
req.session.username = username;
|
||||
|
||||
// TODO: What about dev?
|
||||
res.redirect(302, LOGIN_REDIRECT_URL);
|
||||
})
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
|
@ -56,7 +56,6 @@
|
|||
"prettier": {},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.10.1",
|
||||
"@types/body-parser": "^1.19.5",
|
||||
"@types/cookie-session": "^2.0.48",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/express": "4.17.9",
|
||||
|
|
|
|||
24
yarn.lock
24
yarn.lock
|
|
@ -5925,7 +5925,7 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.20.7"
|
||||
|
||||
"@types/body-parser@*", "@types/body-parser@^1.19.5":
|
||||
"@types/body-parser@*":
|
||||
version "1.19.5"
|
||||
resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz"
|
||||
integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==
|
||||
|
|
@ -5940,13 +5940,6 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/busboy@*":
|
||||
version "1.5.3"
|
||||
resolved "https://registry.npmjs.org/@types/busboy/-/busboy-1.5.3.tgz"
|
||||
integrity sha512-YMBLFN/xBD8bnqywIlGyYqsNFXu6bsiY7h3Ae0kO17qEuTjsqeyYMRPSUDacIKIquws2Y6KjmxAyNx8xB3xQbw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz"
|
||||
|
|
@ -6080,14 +6073,6 @@
|
|||
resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz"
|
||||
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
|
||||
|
||||
"@types/express-fileupload@^1.4.4":
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/@types/express-fileupload/-/express-fileupload-1.5.0.tgz"
|
||||
integrity sha512-Y9v88IC5ItAxkKwfnyIi1y0jSZwTMY4jqXUQLZ3jFhYJlLdRnN919bKBNM8jbVVD2cxywA/uEC1kNNpZQGwx7Q==
|
||||
dependencies:
|
||||
"@types/busboy" "*"
|
||||
"@types/express" "*"
|
||||
|
||||
"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33":
|
||||
version "4.17.43"
|
||||
resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz"
|
||||
|
|
@ -13642,13 +13627,6 @@ express-async-handler@^1.1.4:
|
|||
resolved "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz"
|
||||
integrity sha512-rCSVtPXRmQSW8rmik/AIb2P0op6l7r1fMW538yyvTMltCO4xQEWMmobfrIxN2V1/mVrgxB8Az3reYF6yUZw37w==
|
||||
|
||||
express-fileupload@^1.1.7-alpha.3:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.5.0.tgz"
|
||||
integrity sha512-jSW3w9evqM37VWkEPkL2Ck5wUo2a8qa03MH+Ou/0ZSTpNlQFBvSLjU12k2nYcHhaMPv4JVvv6+Ac1OuLgUZb7w==
|
||||
dependencies:
|
||||
busboy "^1.6.0"
|
||||
|
||||
express-graphql@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue