forked from StellarEdu/StellarEduPay
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathschoolController.js
More file actions
117 lines (107 loc) · 3.38 KB
/
Copy pathschoolController.js
File metadata and controls
117 lines (107 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'use strict';
const crypto = require('crypto');
const School = require('../models/schoolModel');
// POST /api/schools
async function createSchool(req, res, next) {
try {
const { name, slug, stellarAddress, network, adminEmail, address } = req.body;
const errors = [];
if (!name || typeof name !== 'string' || !name.trim())
errors.push('name is required');
if (!slug || !/^[a-z0-9-]{2,60}$/.test(slug.trim().toLowerCase()))
errors.push('slug must be 2–60 lowercase alphanumeric characters or hyphens');
if (!stellarAddress || !/^G[A-Z2-7]{55}$/.test(stellarAddress))
errors.push('stellarAddress must be a valid Stellar public key (starts with G, 56 chars)');
if (network && !['testnet', 'mainnet'].includes(network))
errors.push('network must be "testnet" or "mainnet"');
if (errors.length) return res.status(400).json({ errors, code: 'VALIDATION_ERROR' });
const schoolId = `SCH-${crypto.randomBytes(4).toString('hex').toUpperCase()}`;
const school = await School.create({
schoolId,
name: name.trim(),
slug: slug.trim().toLowerCase(),
stellarAddress,
network: network || 'testnet',
adminEmail: adminEmail || null,
address: address || null,
});
res.status(201).json(school);
} catch (err) {
if (err.code === 11000) {
const field = err.message.includes('slug') ? 'slug' : 'schoolId';
const e = new Error(`A school with this ${field} already exists`);
e.code = 'DUPLICATE_SCHOOL';
e.status = 409;
return next(e);
}
next(err);
}
}
// GET /api/schools
async function getAllSchools(req, res, next) {
try {
const schools = await School.find({ isActive: true }).sort({ name: 1 }).lean();
res.json(schools);
} catch (err) {
next(err);
}
}
// GET /api/schools/:schoolSlug
async function getSchool(req, res, next) {
try {
const school = await School.findOne({
slug: req.params.schoolSlug.toLowerCase(),
isActive: true,
}).lean();
if (!school) {
const e = new Error('School not found');
e.code = 'NOT_FOUND';
return next(e);
}
res.json(school);
} catch (err) {
next(err);
}
}
// PATCH /api/schools/:schoolSlug
async function updateSchool(req, res, next) {
try {
const allowed = ['name', 'stellarAddress', 'network', 'adminEmail', 'address'];
const updates = {};
for (const key of allowed) {
if (req.body[key] !== undefined) updates[key] = req.body[key];
}
const school = await School.findOneAndUpdate(
{ slug: req.params.schoolSlug.toLowerCase(), isActive: true },
updates,
{ new: true, runValidators: true }
);
if (!school) {
const e = new Error('School not found');
e.code = 'NOT_FOUND';
return next(e);
}
res.json(school);
} catch (err) {
next(err);
}
}
// DELETE /api/schools/:schoolSlug (soft-delete)
async function deactivateSchool(req, res, next) {
try {
const school = await School.findOneAndUpdate(
{ slug: req.params.schoolSlug.toLowerCase() },
{ isActive: false },
{ new: true }
);
if (!school) {
const e = new Error('School not found');
e.code = 'NOT_FOUND';
return next(e);
}
res.json({ message: `School "${school.name}" deactivated` });
} catch (err) {
next(err);
}
}
module.exports = { createSchool, getAllSchools, getSchool, updateSchool, deactivateSchool };