Skip to content

Commit b112824

Browse files
authored
Merge pull request #510 from The-Purple-Warehouse/main
[staging deploy] tpb
2 parents 227f512 + baf86a1 commit b112824

10 files changed

Lines changed: 2019 additions & 1 deletion

File tree

helpers/trading.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import TradingListing from "../models/tradingListing";
2+
3+
interface ListingData {
4+
type: string;
5+
team: number;
6+
teamName?: string;
7+
item: string;
8+
category: string;
9+
quantity?: number;
10+
description?: string;
11+
contact: string;
12+
event?: string;
13+
}
14+
15+
interface ListingFilters {
16+
type?: string;
17+
category?: string;
18+
event?: string;
19+
}
20+
21+
export async function addListing(data: ListingData) {
22+
const listing = new TradingListing({
23+
type: data.type,
24+
team: data.team,
25+
teamName: data.teamName || "",
26+
item: data.item,
27+
category: data.category,
28+
quantity: data.quantity || 1,
29+
description: data.description || "",
30+
contact: data.contact,
31+
event: data.event || "general",
32+
timestamp: new Date()
33+
});
34+
await listing.save();
35+
return listing.toObject();
36+
}
37+
38+
export async function getListings(filters?: ListingFilters) {
39+
const query: any = {};
40+
if (filters) {
41+
if (filters.type && filters.type !== "all") {
42+
query.type = filters.type;
43+
}
44+
if (filters.category && filters.category !== "all") {
45+
query.category = filters.category;
46+
}
47+
if (filters.event && filters.event !== "all") {
48+
query.event = filters.event;
49+
}
50+
}
51+
return TradingListing.find(query).sort({ timestamp: -1 }).lean();
52+
}
53+
54+
export async function deleteListing(id: string, team: number) {
55+
const listing: any = await TradingListing.findById(id);
56+
if (!listing) {
57+
throw new Error("Listing not found");
58+
}
59+
if (listing.team !== team) {
60+
throw new Error("You can only delete your own listings");
61+
}
62+
await listing.deleteOne();
63+
return true;
64+
}

index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import resourcesAPIRouter from "./routers/api/resources";
2929
import scoutingRouter from "./routers/scouting";
3030
import scoutingAPIRouter from "./routers/api/scouting";
3131
import tpsAPIRouter from "./routers/api/tps";
32+
import tradingAPIRouter from "./routers/trading";
3233

3334
const app = new Koa();
3435

@@ -136,6 +137,7 @@ if (config.features.includes("scouting")) {
136137
if (config.features.includes("tps")) {
137138
router.use("/api/v1/tps", tpsAPIRouter.routes());
138139
}
140+
router.use("/api/v1/trading", tradingAPIRouter.routes());
139141

140142
function formatNumber(num) {
141143
if (num < 10) {

models/tradingListing.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import mongoose from "../db";
2+
3+
export default mongoose.model(
4+
"TradingListing",
5+
new mongoose.Schema({
6+
type: {
7+
type: String,
8+
enum: ["offer", "request"],
9+
required: true
10+
},
11+
team: {
12+
type: Number,
13+
required: true
14+
},
15+
teamName: String,
16+
item: {
17+
type: String,
18+
required: true
19+
},
20+
category: {
21+
type: String,
22+
required: true
23+
},
24+
quantity: {
25+
type: Number,
26+
default: 1
27+
},
28+
description: String,
29+
contact: {
30+
type: String,
31+
required: true
32+
},
33+
event: String,
34+
timestamp: {
35+
type: Date,
36+
default: Date.now
37+
}
38+
})
39+
);

routers/trading.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import Koa from "koa";
2+
import Router from "koa-router";
3+
import bodyParser from "koa-bodyparser";
4+
import { getTeam } from "../helpers/tba";
5+
import { addListing, getListings, deleteListing } from "../helpers/trading";
6+
import { addAPIHeaders } from "../helpers/utils";
7+
8+
const router = new Router<Koa.DefaultState, Koa.Context>();
9+
10+
router.get("/team/:number", async (ctx) => {
11+
addAPIHeaders(ctx);
12+
try {
13+
const teamNumber = parseInt(ctx.params.number);
14+
if (!teamNumber || teamNumber < 1) {
15+
ctx.body = { success: false, error: "Invalid team number" };
16+
return;
17+
}
18+
const team = await getTeam(teamNumber);
19+
if (team && (team as any).nickname) {
20+
ctx.body = {
21+
success: true,
22+
body: {
23+
team: teamNumber,
24+
teamName: (team as any).nickname
25+
}
26+
};
27+
} else {
28+
ctx.body = { success: false, error: "Team not found" };
29+
}
30+
} catch (err) {
31+
ctx.body = { success: false, error: "Could not look up team" };
32+
}
33+
});
34+
35+
router.get("/listings", async (ctx) => {
36+
addAPIHeaders(ctx);
37+
try {
38+
const query = ctx.query as any;
39+
const listings = await getListings({
40+
type: query.type,
41+
category: query.category,
42+
event: query.event
43+
});
44+
ctx.body = {
45+
success: true,
46+
body: { listings }
47+
};
48+
} catch (err) {
49+
ctx.body = {
50+
success: false,
51+
error: "Could not fetch listings"
52+
};
53+
}
54+
});
55+
56+
router.post("/listings", bodyParser(), async (ctx) => {
57+
addAPIHeaders(ctx);
58+
try {
59+
const body = ctx.request.body as any;
60+
61+
if (
62+
!body.type ||
63+
!body.team ||
64+
!body.item ||
65+
!body.category ||
66+
!body.contact
67+
) {
68+
ctx.body = {
69+
success: false,
70+
error: "Missing required fields: type, team, item, category, contact"
71+
};
72+
return;
73+
}
74+
75+
if (!["offer", "request"].includes(body.type)) {
76+
ctx.body = {
77+
success: false,
78+
error: "Type must be 'offer' or 'request'"
79+
};
80+
return;
81+
}
82+
83+
const listing = await addListing({
84+
type: body.type,
85+
team: parseInt(body.team),
86+
teamName: body.teamName || "",
87+
item: body.item,
88+
category: body.category,
89+
quantity: parseInt(body.quantity) || 1,
90+
description: body.description || "",
91+
contact: body.contact,
92+
event: body.event || "general"
93+
});
94+
95+
ctx.body = {
96+
success: true,
97+
body: {
98+
listing
99+
}
100+
};
101+
} catch (err) {
102+
ctx.body = {
103+
success: false,
104+
error: "Could not create listing"
105+
};
106+
}
107+
});
108+
109+
router.delete("/listings/:id", bodyParser(), async (ctx) => {
110+
addAPIHeaders(ctx);
111+
try {
112+
const body = ctx.request.body as any;
113+
if (!body.team) {
114+
ctx.body = {
115+
success: false,
116+
error: "Team number required"
117+
};
118+
return;
119+
}
120+
await deleteListing(ctx.params.id, parseInt(body.team));
121+
ctx.body = { success: true, body: { message: "Listing deleted" } };
122+
} catch (err) {
123+
ctx.body = {
124+
success: false,
125+
error: err.message || "Could not delete listing"
126+
};
127+
}
128+
});
129+
130+
export default router;

0 commit comments

Comments
 (0)