seed data
This commit is contained in:
1 parent
edee33416f
commit
364284553b
5 files changed
+128
-47
No files matched your search
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"nate": [
|
||||||
|
"Perfect Round",
|
||||||
|
"No Misses",
|
||||||
|
"Accuracy Master",
|
||||||
|
"Practice Streak",
|
||||||
|
"Vocabulary Pro"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"student1": [
|
||||||
|
{
|
||||||
|
"score": 8,
|
||||||
|
"completed": true,
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"word": "apple",
|
||||||
|
"correct": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"word": "banana",
|
||||||
|
"correct": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"word": "grape",
|
||||||
|
"correct": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-07-07T12:14:21.057Z"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nate": [
|
||||||
|
{
|
||||||
|
"score": 1,
|
||||||
|
"completed": true,
|
||||||
|
"answers": [
|
||||||
|
{
|
||||||
|
"word": "not",
|
||||||
|
"correct": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"word": "bot",
|
||||||
|
"correct": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"word": "snot",
|
||||||
|
"correct": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": "2025-07-07T12:14:21.058Z"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"username": "admin1",
|
||||||
|
"hash": "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8",
|
||||||
|
"role": "admin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "nate",
|
||||||
|
"hash": "5a2a558c78d3717db731600c4f354fa1d9c84b556f108091a891f444f1bdec40",
|
||||||
|
"role": "student"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"username": "student1",
|
||||||
|
"hash": "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92",
|
||||||
|
"role": "student"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"student1": [],
|
||||||
|
"admin1": [
|
||||||
|
"boy",
|
||||||
|
"bot",
|
||||||
|
"snot"
|
||||||
|
],
|
||||||
|
"nate": [
|
||||||
|
"not",
|
||||||
|
"bot",
|
||||||
|
"snot"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -8,10 +8,12 @@ const PORT = 3000;
|
|||||||
app.use(express.static('public'));
|
app.use(express.static('public'));
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
// 🗂 Ensure data directory and files exist
|
// 🗂 Directories
|
||||||
const DATA_DIR = path.join(__dirname, 'data');
|
const DATA_DIR = path.join(__dirname, 'data');
|
||||||
|
const SEED_DIR = path.join(__dirname, 'seed');
|
||||||
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR);
|
if (!fs.existsSync(DATA_DIR)) fs.mkdirSync(DATA_DIR);
|
||||||
|
|
||||||
|
// 🗃 File mapping
|
||||||
const files = {
|
const files = {
|
||||||
users: 'users.json',
|
users: 'users.json',
|
||||||
wordlists: 'wordlists.json',
|
wordlists: 'wordlists.json',
|
||||||
@@ -19,16 +21,28 @@ const files = {
|
|||||||
badges: 'badges.json'
|
badges: 'badges.json'
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const file of Object.values(files)) {
|
// 🌱 Initialize data from seeds if missing/empty
|
||||||
const target = path.join(DATA_DIR, file);
|
function ensureFileWithSeed(name, fallback = '{}') {
|
||||||
if (!fs.existsSync(target)) {
|
const targetPath = path.join(DATA_DIR, name);
|
||||||
const defaultData = file === 'users.json' ? '[]' : '{}';
|
const seedPath = path.join(SEED_DIR, name);
|
||||||
fs.writeFileSync(target, defaultData);
|
|
||||||
console.log(`📄 Initialized ${file}`);
|
if (!fs.existsSync(targetPath) || fs.readFileSync(targetPath, 'utf-8').trim() === '') {
|
||||||
|
if (fs.existsSync(seedPath)) {
|
||||||
|
fs.copyFileSync(seedPath, targetPath);
|
||||||
|
console.log(`🌱 Seeded ${name} from seed/${name}`);
|
||||||
|
} else {
|
||||||
|
fs.writeFileSync(targetPath, fallback);
|
||||||
|
console.log(`📄 Initialized ${name} with default`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🛡 Safe JSON reader
|
for (const [key, file] of Object.entries(files)) {
|
||||||
|
const defaultData = file === 'users.json' ? '[]' : '{}';
|
||||||
|
ensureFileWithSeed(file, defaultData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔐 JSON utility
|
||||||
function readJsonSafe(filePath, fallback = {}) {
|
function readJsonSafe(filePath, fallback = {}) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
||||||
@@ -37,41 +51,31 @@ function readJsonSafe(filePath, fallback = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🔐 Verify user credentials
|
// 🔐 Verify login
|
||||||
app.post('/verifyUser', (req, res) => {
|
app.post('/verifyUser', (req, res) => {
|
||||||
const { username, hash } = req.body;
|
const { username, hash } = req.body;
|
||||||
if (typeof username !== 'string' || typeof hash !== 'string') {
|
if (typeof username !== 'string' || typeof hash !== 'string') {
|
||||||
return res.status(400).send("Invalid credentials format");
|
return res.status(400).send("Invalid credentials format");
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
||||||
const user = users.find(u => u.username === username && u.hash === hash);
|
const user = users.find(u => u.username === username && u.hash === hash);
|
||||||
|
user ? res.json(user) : res.status(401).send("Invalid login");
|
||||||
if (user) {
|
|
||||||
res.json(user);
|
|
||||||
} else {
|
|
||||||
res.status(401).send("Invalid login");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// ➕ Add new user
|
// ➕ Add user
|
||||||
app.post('/addUser', (req, res) => {
|
app.post('/addUser', (req, res) => {
|
||||||
const { username, hash, role } = req.body;
|
const { username, hash, role } = req.body;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof username !== 'string' ||
|
typeof username !== 'string' ||
|
||||||
typeof hash !== 'string' ||
|
typeof hash !== 'string' ||
|
||||||
typeof role !== 'string' ||
|
typeof role !== 'string' ||
|
||||||
!['admin', 'student'].includes(role.trim().toLowerCase())
|
!['admin', 'student'].includes(role.trim().toLowerCase())
|
||||||
) {
|
) return res.status(400).send("Invalid user data");
|
||||||
return res.status(400).send("Invalid user data");
|
|
||||||
}
|
|
||||||
|
|
||||||
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
||||||
if (users.find(u => u.username === username)) {
|
if (users.find(u => u.username === username)) {
|
||||||
return res.status(409).send("User already exists");
|
return res.status(409).send("User already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
users.push({ username: username.trim(), hash, role: role.trim() });
|
users.push({ username: username.trim(), hash, role: role.trim() });
|
||||||
fs.writeFileSync(path.join(DATA_DIR, files.users), JSON.stringify(users, null, 2));
|
fs.writeFileSync(path.join(DATA_DIR, files.users), JSON.stringify(users, null, 2));
|
||||||
res.send(`✅ User "${username}" added`);
|
res.send(`✅ User "${username}" added`);
|
||||||
@@ -89,34 +93,28 @@ app.post('/deleteUser', (req, res) => {
|
|||||||
if (users.length === updated.length) {
|
if (users.length === updated.length) {
|
||||||
return res.status(404).send("User not found");
|
return res.status(404).send("User not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.writeFileSync(usersPath, JSON.stringify(updated, null, 2));
|
fs.writeFileSync(usersPath, JSON.stringify(updated, null, 2));
|
||||||
res.send(`✅ User "${username}" deleted`);
|
res.send(`✅ User "${username}" deleted`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 📚 Get word list
|
// 📚 Word list
|
||||||
app.get('/getWordList', (req, res) => {
|
app.get('/getWordList', (req, res) => {
|
||||||
const username = req.query.user;
|
const username = req.query.user;
|
||||||
if (!username || typeof username !== 'string') {
|
if (!username || typeof username !== 'string') {
|
||||||
return res.status(400).send("Username required");
|
return res.status(400).send("Username required");
|
||||||
}
|
}
|
||||||
|
|
||||||
const wordlists = readJsonSafe(path.join(DATA_DIR, files.wordlists));
|
const wordlists = readJsonSafe(path.join(DATA_DIR, files.wordlists));
|
||||||
res.json(wordlists[username] || []);
|
res.json(wordlists[username] || []);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 💾 Save word list
|
|
||||||
app.post('/saveWordList', (req, res) => {
|
app.post('/saveWordList', (req, res) => {
|
||||||
const { username, words } = req.body;
|
const { username, words } = req.body;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof username !== 'string' ||
|
typeof username !== 'string' ||
|
||||||
!Array.isArray(words) ||
|
!Array.isArray(words) ||
|
||||||
words.length > 100 ||
|
words.length > 100 ||
|
||||||
words.some(w => typeof w !== 'string')
|
words.some(w => typeof w !== 'string')
|
||||||
) {
|
) return res.status(400).send("Invalid word list");
|
||||||
return res.status(400).send("Invalid word list");
|
|
||||||
}
|
|
||||||
|
|
||||||
const userList = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
const userList = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
||||||
if (!userList.find(u => u.username === username)) {
|
if (!userList.find(u => u.username === username)) {
|
||||||
@@ -130,16 +128,12 @@ app.post('/saveWordList', (req, res) => {
|
|||||||
res.send(`✅ Word list saved for ${username}`);
|
res.send(`✅ Word list saved for ${username}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 📊 Get results
|
// 📊 Results
|
||||||
app.get('/getResults', (req, res) => {
|
app.get('/getResults', (req, res) => {
|
||||||
const results = readJsonSafe(path.join(DATA_DIR, files.results));
|
const results = readJsonSafe(path.join(DATA_DIR, files.results));
|
||||||
res.json(results);
|
res.json(results);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 🚀 Launch server
|
|
||||||
app.listen(PORT, () => {
|
|
||||||
console.log(`✅ Server listening at http://localhost:${PORT}`);
|
|
||||||
});
|
|
||||||
app.post('/saveResults', (req, res) => {
|
app.post('/saveResults', (req, res) => {
|
||||||
const { username, result } = req.body;
|
const { username, result } = req.body;
|
||||||
if (
|
if (
|
||||||
@@ -152,31 +146,30 @@ app.post('/saveResults', (req, res) => {
|
|||||||
|
|
||||||
const resultsPath = path.join(DATA_DIR, files.results);
|
const resultsPath = path.join(DATA_DIR, files.results);
|
||||||
const allResults = readJsonSafe(resultsPath);
|
const allResults = readJsonSafe(resultsPath);
|
||||||
|
|
||||||
if (!allResults[username]) allResults[username] = [];
|
if (!allResults[username]) allResults[username] = [];
|
||||||
|
|
||||||
allResults[username].push({
|
allResults[username].push({
|
||||||
...result,
|
...result,
|
||||||
timestamp: new Date().toISOString()
|
timestamp: new Date().toISOString()
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(resultsPath, JSON.stringify(allResults, null, 2));
|
fs.writeFileSync(resultsPath, JSON.stringify(allResults, null, 2));
|
||||||
res.send(`✅ Results archived for ${username}`);
|
res.send(`✅ Results archived for ${username}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 👥 Get users
|
||||||
app.get('/getUsers', (req, res) => {
|
app.get('/getUsers', (req, res) => {
|
||||||
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
const users = readJsonSafe(path.join(DATA_DIR, files.users), []);
|
||||||
res.json(users);
|
res.json(users);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 🎖 Badges
|
||||||
const badgePath = path.join(DATA_DIR, files.badges);
|
|
||||||
app.post('/awardBadges', (req, res) => {
|
app.post('/awardBadges', (req, res) => {
|
||||||
const { username, badges } = req.body;
|
const { username, badges } = req.body;
|
||||||
if (
|
if (typeof username !== 'string' || !Array.isArray(badges)) {
|
||||||
typeof username !== 'string' ||
|
return res.status(400).send("Invalid badge format");
|
||||||
!Array.isArray(badges)
|
}
|
||||||
) return res.status(400).send("Invalid badge format");
|
|
||||||
|
|
||||||
|
const badgePath = path.join(DATA_DIR, files.badges);
|
||||||
const allBadges = readJsonSafe(badgePath);
|
const allBadges = readJsonSafe(badgePath);
|
||||||
if (!allBadges[username]) allBadges[username] = [];
|
if (!allBadges[username]) allBadges[username] = [];
|
||||||
|
|
||||||
@@ -185,12 +178,17 @@ app.post('/awardBadges', (req, res) => {
|
|||||||
allBadges[username].push(b);
|
allBadges[username].push(b);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(badgePath, JSON.stringify(allBadges, null, 2));
|
fs.writeFileSync(badgePath, JSON.stringify(allBadges, null, 2));
|
||||||
res.send(`🎉 Badges updated for ${username}`);
|
res.send(`🎉 Badges updated for ${username}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/getBadges', (req, res) => {
|
app.get('/getBadges', (req, res) => {
|
||||||
const badgePath = path.join(DATA_DIR, files.badges);
|
const badgePath = path.join(DATA_DIR, files.badges);
|
||||||
const badges = readJsonSafe(badgePath);
|
const badges = readJsonSafe(badgePath);
|
||||||
res.json(badges);
|
res.json(badges);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 🚀 Server start
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`✅ Server listening at http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user