@vatvaghool/create-ipl-dashboard 0.1.24 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/generate-template.mjs +1 -0
- package/src/index.mjs +2 -5
- package/src/prompts.mjs +2 -2
- package/src/scaffold.mjs +0 -8
- package/template/app/lib/config.ts +1 -1
- package/template/app/utils/dashboard/index.ts +1 -10
- package/template/packages/ipl-dashboard-utils/src/transform.ts +1 -11
- package/template/scripts/monitor-ops-status.sh +0 -50
- package/template/scripts/seed-mongodb.ts +0 -115
- package/template/scripts/verify-production.mjs +0 -108
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
|
-
import { mkdir } from "node:fs/promises";
|
|
6
|
-
import { getProjectName, getLeagueName, getTeams
|
|
5
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
6
|
+
import { getProjectName, getLeagueName, getTeams } from "./prompts.mjs";
|
|
7
7
|
import { scaffoldProject } from "./scaffold.mjs";
|
|
8
8
|
|
|
9
9
|
function printHelp(exitCode) {
|
|
@@ -48,7 +48,6 @@ async function main() {
|
|
|
48
48
|
const projectPath = join(process.cwd(), projectName);
|
|
49
49
|
|
|
50
50
|
if (existsSync(projectPath)) {
|
|
51
|
-
const { rm } = await import("node:fs/promises");
|
|
52
51
|
console.log(` Directory "${projectName}" already exists. Removing...`);
|
|
53
52
|
await rm(projectPath, { recursive: true, force: true });
|
|
54
53
|
}
|
|
@@ -72,8 +71,6 @@ async function main() {
|
|
|
72
71
|
];
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
close();
|
|
76
|
-
|
|
77
74
|
await scaffoldProject(projectPath, { leagueName, teams, skipInstall: flags.skipInstall });
|
|
78
75
|
|
|
79
76
|
console.log("");
|
package/src/prompts.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createInterface } from "node:readline";
|
|
2
|
-
import { stdin as input,
|
|
2
|
+
import { stdin as input, stdin } from "node:process";
|
|
3
3
|
|
|
4
4
|
const isTTY = stdin.isTTY;
|
|
5
5
|
|
|
@@ -65,4 +65,4 @@ export async function getTeams() {
|
|
|
65
65
|
return parseTeams(answer);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
|
package/src/scaffold.mjs
CHANGED
|
@@ -25,7 +25,6 @@ export async function scaffoldProject(
|
|
|
25
25
|
for (const f of [
|
|
26
26
|
"AGENTS.md",
|
|
27
27
|
"tsconfig.tsbuildinfo",
|
|
28
|
-
"tsconfig.buildinfo",
|
|
29
28
|
"package-lock.json",
|
|
30
29
|
]) {
|
|
31
30
|
try {
|
|
@@ -51,13 +50,6 @@ export async function scaffoldProject(
|
|
|
51
50
|
if (!skipInstall) {
|
|
52
51
|
console.log(" Running npm install...");
|
|
53
52
|
execSync("npm install", { cwd: projectPath, stdio: "inherit" });
|
|
54
|
-
|
|
55
|
-
console.log(" Installing Playwright browser...");
|
|
56
|
-
try {
|
|
57
|
-
execSync("npx playwright install chromium", { cwd: projectPath, stdio: "inherit" });
|
|
58
|
-
} catch {
|
|
59
|
-
console.log(" Playwright install skipped (run manually: npx playwright install chromium)");
|
|
60
|
-
}
|
|
61
53
|
} else {
|
|
62
54
|
console.log(
|
|
63
55
|
" Skipping npm install (run manually: cd <project> && npm install)",
|
|
@@ -3,7 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
export const config = {
|
|
4
4
|
mongodb: {
|
|
5
5
|
databaseName: process.env.IPL_DB_NAME || "ipl",
|
|
6
|
-
collectionName: process.env.IPL_COLLECTION_NAME || "ipl",
|
|
6
|
+
collectionName: process.env.COLLECTION_NAME || process.env.IPL_COLLECTION_NAME || "ipl",
|
|
7
7
|
},
|
|
8
8
|
|
|
9
9
|
league: {
|
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import { getChartColor } from "../../lib/utils/getChartColor";
|
|
2
|
-
|
|
3
|
-
export function formatCompactNumber(value: number): string {
|
|
4
|
-
if (value >= 1000000) {
|
|
5
|
-
return `${(value / 1000000).toFixed(1)}M`;
|
|
6
|
-
}
|
|
7
|
-
if (value >= 1000) {
|
|
8
|
-
return `${(value / 1000).toFixed(1)}K`;
|
|
9
|
-
}
|
|
10
|
-
return value.toString();
|
|
11
|
-
}
|
|
2
|
+
export { formatCompactNumber } from "../../lib/utils";
|
|
12
3
|
|
|
13
4
|
export function formatLedgerNumber(value?: number): string {
|
|
14
5
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
@@ -13,17 +13,7 @@ import type {
|
|
|
13
13
|
const DEFAULT_TOTAL_TRANSFERS = 160;
|
|
14
14
|
const FLOAT_TOLERANCE = 0.01;
|
|
15
15
|
|
|
16
|
-
const RAW_TEAM_ALIASES: Record<string, string> = {
|
|
17
|
-
"Pankaj Konde": "PKs11",
|
|
18
|
-
"Rishikesh Shinde": "Watapi",
|
|
19
|
-
"Rushabh Shah": "RushS01",
|
|
20
|
-
"Vijay Swami": "VATVAGHOOL XI",
|
|
21
|
-
"Aditya Raut": "SquadSeven9",
|
|
22
|
-
"Nilesh Birajdar": "Nilesh Birajdar",
|
|
23
|
-
"Ravi Kiran Guthula": "RKs Stallions",
|
|
24
|
-
"Rahul Sharma": "RSAwesome 11",
|
|
25
|
-
"Raviteja Jakkani": "Bat Bowl XI",
|
|
26
|
-
};
|
|
16
|
+
const RAW_TEAM_ALIASES: Record<string, string> = {};
|
|
27
17
|
|
|
28
18
|
const resolveTeamName = (
|
|
29
19
|
name: string,
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
|
|
5
|
-
APP_BASE_URL="${APP_BASE_URL:-}"
|
|
6
|
-
|
|
7
|
-
if [ -z "${APP_BASE_URL}" ]; then
|
|
8
|
-
echo "APP_BASE_URL is required. Example: APP_BASE_URL=https://your-app-domain" >&2
|
|
9
|
-
exit 1
|
|
10
|
-
fi
|
|
11
|
-
|
|
12
|
-
URL="${APP_BASE_URL%/}/api/ops/status"
|
|
13
|
-
TMP_BODY="$(mktemp)"
|
|
14
|
-
TMP_HEADERS="$(mktemp)"
|
|
15
|
-
|
|
16
|
-
cleanup() {
|
|
17
|
-
rm -f "${TMP_BODY}" "${TMP_HEADERS}"
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
trap cleanup EXIT
|
|
21
|
-
|
|
22
|
-
HTTP_CODE="$(
|
|
23
|
-
curl -sS \
|
|
24
|
-
-D "${TMP_HEADERS}" \
|
|
25
|
-
-o "${TMP_BODY}" \
|
|
26
|
-
-w "%{http_code}" \
|
|
27
|
-
-H "Accept: application/json" \
|
|
28
|
-
"${URL}"
|
|
29
|
-
)"
|
|
30
|
-
|
|
31
|
-
if [ "${HTTP_CODE}" -ge 500 ]; then
|
|
32
|
-
echo "monitor failure: ${URL} returned HTTP ${HTTP_CODE}" >&2
|
|
33
|
-
cat "${TMP_BODY}" >&2
|
|
34
|
-
exit 1
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
if grep -q '"status":"critical"' "${TMP_BODY}"; then
|
|
38
|
-
echo "monitor failure: critical app status reported by ${URL}" >&2
|
|
39
|
-
cat "${TMP_BODY}" >&2
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
if grep -q '"status":"degraded"' "${TMP_BODY}"; then
|
|
44
|
-
echo "monitor warning: degraded app status reported by ${URL}" >&2
|
|
45
|
-
cat "${TMP_BODY}"
|
|
46
|
-
exit 2
|
|
47
|
-
fi
|
|
48
|
-
|
|
49
|
-
echo "monitor ok: ${URL} returned HTTP ${HTTP_CODE}"
|
|
50
|
-
cat "${TMP_BODY}"
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { MongoClient } from "mongodb";
|
|
2
|
-
import {
|
|
3
|
-
buildManualDashboard,
|
|
4
|
-
normalizeRawApiUsers,
|
|
5
|
-
} from "../app/api/ipl/transform.ts";
|
|
6
|
-
import { rawApiUsers } from "../app/api/ipl/data.ts";
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
process.loadEnvFile(".env");
|
|
10
|
-
} catch {
|
|
11
|
-
// Environment variables may already be provided by the shell.
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const DB_NAME = "ipl";
|
|
15
|
-
const COLLECTION_NAME = "ipl";
|
|
16
|
-
const RAW_USERS_DOCUMENT_TYPE = "raw-users";
|
|
17
|
-
|
|
18
|
-
const args = new Set(process.argv.slice(2));
|
|
19
|
-
const dryRun = args.has("--dry-run");
|
|
20
|
-
const force = args.has("--force");
|
|
21
|
-
const reset = args.has("--reset");
|
|
22
|
-
|
|
23
|
-
const loadSeedUsers = () => {
|
|
24
|
-
const users = normalizeRawApiUsers(rawApiUsers);
|
|
25
|
-
|
|
26
|
-
if (!users) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
"Failed to normalize raw seed users from app/api/ipl/data.ts",
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return users;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const createRawUsersDocument = (users: ReturnType<typeof loadSeedUsers>) => ({
|
|
36
|
-
type: RAW_USERS_DOCUMENT_TYPE,
|
|
37
|
-
updatedAt: new Date().toISOString(),
|
|
38
|
-
users: users.map((user) => ({
|
|
39
|
-
rno: user.rno,
|
|
40
|
-
temname: user.temname,
|
|
41
|
-
points: user.points,
|
|
42
|
-
matches: [...user.matches].sort((a, b) => a.matchId - b.matchId),
|
|
43
|
-
})),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const logPlan = () => {
|
|
47
|
-
const users = loadSeedUsers();
|
|
48
|
-
const dashboard = buildManualDashboard(users);
|
|
49
|
-
|
|
50
|
-
console.log(`Seed users: ${users.length}`);
|
|
51
|
-
console.log(
|
|
52
|
-
`Latest match id: ${Math.max(
|
|
53
|
-
...users.flatMap((user) => user.matches.map((match) => match.matchId)),
|
|
54
|
-
)}`,
|
|
55
|
-
);
|
|
56
|
-
console.log(`Reset requested: ${reset ? "yes" : "no"}`);
|
|
57
|
-
console.log(`Dashboard rows: ${dashboard.daily.length}`);
|
|
58
|
-
console.log(`Leaderboard rows: ${dashboard.overall.length}`);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
const run = async () => {
|
|
62
|
-
if (dryRun) {
|
|
63
|
-
logPlan();
|
|
64
|
-
console.log("Dry run only. No MongoDB writes performed.");
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const uri = process.env.MONGODB_URI;
|
|
69
|
-
|
|
70
|
-
if (!uri) {
|
|
71
|
-
throw new Error("MONGODB_URI is required to seed MongoDB.");
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const users = loadSeedUsers();
|
|
75
|
-
const client = new MongoClient(uri);
|
|
76
|
-
|
|
77
|
-
try {
|
|
78
|
-
await client.connect();
|
|
79
|
-
|
|
80
|
-
const collection = client.db(DB_NAME).collection(COLLECTION_NAME);
|
|
81
|
-
|
|
82
|
-
if (reset) {
|
|
83
|
-
const result = await collection.deleteMany({});
|
|
84
|
-
console.log(`Deleted ${result.deletedCount} IPL MongoDB documents.`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const existingRawUsers = await collection.findOne({
|
|
88
|
-
type: RAW_USERS_DOCUMENT_TYPE,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (!existingRawUsers || force || reset) {
|
|
92
|
-
await collection.updateOne(
|
|
93
|
-
{ type: RAW_USERS_DOCUMENT_TYPE },
|
|
94
|
-
{ $set: createRawUsersDocument(users) },
|
|
95
|
-
{ upsert: true },
|
|
96
|
-
);
|
|
97
|
-
console.log(
|
|
98
|
-
existingRawUsers
|
|
99
|
-
? "Updated raw-users document from local seed data."
|
|
100
|
-
: "Inserted raw-users document from local seed data.",
|
|
101
|
-
);
|
|
102
|
-
} else {
|
|
103
|
-
console.log(
|
|
104
|
-
"Skipped raw-users document because it already exists. Use --force to overwrite.",
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
} finally {
|
|
108
|
-
await client.close();
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
run().catch((error) => {
|
|
113
|
-
console.error(error instanceof Error ? error.message : error);
|
|
114
|
-
process.exitCode = 1;
|
|
115
|
-
});
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
const readEnv = (name, fallback = "") => process.env[name]?.trim() || fallback;
|
|
2
|
-
|
|
3
|
-
const baseUrl = readEnv("APP_BASE_URL");
|
|
4
|
-
|
|
5
|
-
if (!baseUrl) {
|
|
6
|
-
throw new Error("APP_BASE_URL is required. Example: https://your-app-domain");
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
const normalizedBaseUrl = baseUrl.replace(/\/$/, "");
|
|
10
|
-
|
|
11
|
-
const checks = [
|
|
12
|
-
{
|
|
13
|
-
name: "ops status",
|
|
14
|
-
path: "/api/ops/status",
|
|
15
|
-
validate: (payload, response) => {
|
|
16
|
-
if (!payload || typeof payload !== "object") {
|
|
17
|
-
throw new Error("Invalid JSON payload.");
|
|
18
|
-
}
|
|
19
|
-
if (!("mongoConfigured" in payload)) {
|
|
20
|
-
throw new Error("Missing mongoConfigured field.");
|
|
21
|
-
}
|
|
22
|
-
if (!response.ok) {
|
|
23
|
-
const statusLabel =
|
|
24
|
-
payload && typeof payload === "object" && "status" in payload
|
|
25
|
-
? String(payload.status ?? "unknown")
|
|
26
|
-
: "unknown";
|
|
27
|
-
throw new Error(
|
|
28
|
-
`Ops status reported HTTP ${response.status} with status ${statusLabel}.`,
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: "dashboard",
|
|
35
|
-
path: "/api/ipl",
|
|
36
|
-
validate: (payload) => {
|
|
37
|
-
if (!payload || typeof payload !== "object") {
|
|
38
|
-
throw new Error("Invalid JSON payload.");
|
|
39
|
-
}
|
|
40
|
-
if (!("overall" in payload) && !("error" in payload)) {
|
|
41
|
-
throw new Error("Expected overall data or error payload.");
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: "snapshot",
|
|
47
|
-
path: "/api/ipl?format=snapshot",
|
|
48
|
-
validate: (payload) => {
|
|
49
|
-
if (!payload || typeof payload !== "object") {
|
|
50
|
-
throw new Error("Invalid JSON payload.");
|
|
51
|
-
}
|
|
52
|
-
if (!("leaders" in payload) && !("error" in payload)) {
|
|
53
|
-
throw new Error("Expected leaders data or error payload.");
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: "transfers",
|
|
59
|
-
path: "/api/ipl/transfers",
|
|
60
|
-
validate: (payload) => {
|
|
61
|
-
if (!payload || typeof payload !== "object") {
|
|
62
|
-
throw new Error("Invalid JSON payload.");
|
|
63
|
-
}
|
|
64
|
-
if (!("teams" in payload) && !("error" in payload)) {
|
|
65
|
-
throw new Error("Expected transfer snapshot or error payload.");
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
const run = async () => {
|
|
72
|
-
for (const check of checks) {
|
|
73
|
-
const url = `${normalizedBaseUrl}${check.path}`;
|
|
74
|
-
const response = await fetch(url, {
|
|
75
|
-
headers: {
|
|
76
|
-
Accept: "application/json",
|
|
77
|
-
},
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const rawBody = await response.text();
|
|
81
|
-
let payload;
|
|
82
|
-
try {
|
|
83
|
-
payload = JSON.parse(rawBody);
|
|
84
|
-
} catch {
|
|
85
|
-
throw new Error(`[verify-production] ${check.name} returned non-JSON: ${rawBody}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
check.validate(payload, response);
|
|
89
|
-
|
|
90
|
-
console.log(
|
|
91
|
-
JSON.stringify(
|
|
92
|
-
{
|
|
93
|
-
check: check.name,
|
|
94
|
-
url,
|
|
95
|
-
status: response.status,
|
|
96
|
-
ok: response.ok,
|
|
97
|
-
},
|
|
98
|
-
null,
|
|
99
|
-
2,
|
|
100
|
-
),
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
run().catch((error) => {
|
|
106
|
-
console.error(error instanceof Error ? error.message : error);
|
|
107
|
-
process.exitCode = 1;
|
|
108
|
-
});
|