securepool 1.0.0
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/.dockerignore +7 -0
- package/.env.example +20 -0
- package/ARCHITECTURE.md +279 -0
- package/DEPLOYMENT.md +441 -0
- package/README.md +283 -0
- package/SETUP.md +388 -0
- package/apps/demo-backend/Dockerfile +33 -0
- package/apps/demo-backend/package.json +19 -0
- package/apps/demo-backend/src/index.ts +71 -0
- package/apps/demo-backend/tsconfig.json +8 -0
- package/apps/demo-frontend/.env.example +2 -0
- package/apps/demo-frontend/README.md +73 -0
- package/apps/demo-frontend/eslint.config.js +23 -0
- package/apps/demo-frontend/index.html +13 -0
- package/apps/demo-frontend/package.json +24 -0
- package/apps/demo-frontend/public/favicon.svg +1 -0
- package/apps/demo-frontend/public/icons.svg +24 -0
- package/apps/demo-frontend/src/App.tsx +33 -0
- package/apps/demo-frontend/src/assets/hero.png +0 -0
- package/apps/demo-frontend/src/assets/vite.svg +1 -0
- package/apps/demo-frontend/src/components/AccountSwitcher.tsx +373 -0
- package/apps/demo-frontend/src/components/ChangePasswordModal.tsx +128 -0
- package/apps/demo-frontend/src/index.css +272 -0
- package/apps/demo-frontend/src/main.tsx +10 -0
- package/apps/demo-frontend/src/pages/DashboardPage.tsx +141 -0
- package/apps/demo-frontend/src/pages/ForgotPasswordPage.tsx +183 -0
- package/apps/demo-frontend/src/pages/LoginPage.tsx +158 -0
- package/apps/demo-frontend/src/pages/OtpLoginPage.tsx +114 -0
- package/apps/demo-frontend/src/pages/SignupPage.tsx +95 -0
- package/apps/demo-frontend/src/pages/VerifyEmailPage.tsx +84 -0
- package/apps/demo-frontend/tsconfig.app.json +28 -0
- package/apps/demo-frontend/tsconfig.json +7 -0
- package/apps/demo-frontend/tsconfig.node.json +26 -0
- package/apps/demo-frontend/vite.config.ts +15 -0
- package/docs/DATABASE_MONGODB.md +280 -0
- package/docs/DATABASE_SQL.md +472 -0
- package/package.json +21 -0
- package/packages/api/package.json +30 -0
- package/packages/api/src/createSecurePool.ts +113 -0
- package/packages/api/src/index.ts +8 -0
- package/packages/api/src/middleware/authMiddleware.ts +26 -0
- package/packages/api/src/middleware/authorize.ts +24 -0
- package/packages/api/src/middleware/rateLimiter.ts +25 -0
- package/packages/api/src/middleware/tenantMiddleware.ts +12 -0
- package/packages/api/src/routes/authRoutes.ts +229 -0
- package/packages/api/src/routes/sessionRoutes.ts +30 -0
- package/packages/api/src/swagger.ts +529 -0
- package/packages/api/tsconfig.json +8 -0
- package/packages/application/package.json +16 -0
- package/packages/application/src/index.ts +17 -0
- package/packages/application/src/interfaces/IAuditLogRepository.ts +6 -0
- package/packages/application/src/interfaces/IAuthPlugin.ts +4 -0
- package/packages/application/src/interfaces/IEmailService.ts +3 -0
- package/packages/application/src/interfaces/IGoogleAuthService.ts +3 -0
- package/packages/application/src/interfaces/IOtpRepository.ts +8 -0
- package/packages/application/src/interfaces/IOtpService.ts +4 -0
- package/packages/application/src/interfaces/IPasswordHasher.ts +4 -0
- package/packages/application/src/interfaces/IRoleRepository.ts +8 -0
- package/packages/application/src/interfaces/ISessionRepository.ts +8 -0
- package/packages/application/src/interfaces/ITokenRepository.ts +9 -0
- package/packages/application/src/interfaces/ITokenService.ts +5 -0
- package/packages/application/src/interfaces/IUserRepository.ts +8 -0
- package/packages/application/src/services/AuthService.ts +323 -0
- package/packages/application/src/services/RefreshTokenService.ts +53 -0
- package/packages/application/tsconfig.json +8 -0
- package/packages/core/package.json +13 -0
- package/packages/core/src/entities/AuditLog.ts +11 -0
- package/packages/core/src/entities/OtpCode.ts +10 -0
- package/packages/core/src/entities/RefreshToken.ts +9 -0
- package/packages/core/src/entities/Role.ts +6 -0
- package/packages/core/src/entities/Session.ts +10 -0
- package/packages/core/src/entities/Tenant.ts +7 -0
- package/packages/core/src/entities/User.ts +10 -0
- package/packages/core/src/entities/UserRole.ts +6 -0
- package/packages/core/src/enums/index.ts +22 -0
- package/packages/core/src/index.ts +10 -0
- package/packages/core/tsconfig.json +8 -0
- package/packages/infrastructure/package.json +24 -0
- package/packages/infrastructure/src/email/NodemailerEmailService.ts +55 -0
- package/packages/infrastructure/src/google/GoogleAuthServiceImpl.ts +28 -0
- package/packages/infrastructure/src/hashing/BcryptHasher.ts +18 -0
- package/packages/infrastructure/src/index.ts +6 -0
- package/packages/infrastructure/src/jwt/JwtTokenService.ts +32 -0
- package/packages/infrastructure/src/otp/OtpServiceImpl.ts +50 -0
- package/packages/infrastructure/tsconfig.json +8 -0
- package/packages/persistence/package.json +22 -0
- package/packages/persistence/prisma/schema.prisma +88 -0
- package/packages/persistence/src/factory.ts +48 -0
- package/packages/persistence/src/index.ts +30 -0
- package/packages/persistence/src/mongo/connection.ts +9 -0
- package/packages/persistence/src/mongo/models/AuditLogModel.ts +21 -0
- package/packages/persistence/src/mongo/models/OtpModel.ts +19 -0
- package/packages/persistence/src/mongo/models/RefreshTokenModel.ts +17 -0
- package/packages/persistence/src/mongo/models/RoleModel.ts +11 -0
- package/packages/persistence/src/mongo/models/SessionModel.ts +19 -0
- package/packages/persistence/src/mongo/models/UserModel.ts +21 -0
- package/packages/persistence/src/mongo/models/UserRoleModel.ts +15 -0
- package/packages/persistence/src/mongo/repositories/MongoAuditLogRepository.ts +29 -0
- package/packages/persistence/src/mongo/repositories/MongoOtpRepository.ts +34 -0
- package/packages/persistence/src/mongo/repositories/MongoRoleRepository.ts +32 -0
- package/packages/persistence/src/mongo/repositories/MongoSessionRepository.ts +29 -0
- package/packages/persistence/src/mongo/repositories/MongoTokenRepository.ts +34 -0
- package/packages/persistence/src/mongo/repositories/MongoUserRepository.ts +37 -0
- package/packages/persistence/src/prisma/repositories/PrismaAuditLogRepository.ts +37 -0
- package/packages/persistence/src/prisma/repositories/PrismaOtpRepository.ts +43 -0
- package/packages/persistence/src/prisma/repositories/PrismaRoleRepository.ts +36 -0
- package/packages/persistence/src/prisma/repositories/PrismaSessionRepository.ts +39 -0
- package/packages/persistence/src/prisma/repositories/PrismaTokenRepository.ts +50 -0
- package/packages/persistence/src/prisma/repositories/PrismaUserRepository.ts +45 -0
- package/packages/persistence/tsconfig.json +8 -0
- package/packages/react-sdk/package.json +23 -0
- package/packages/react-sdk/src/components/GoogleLoginButton.tsx +54 -0
- package/packages/react-sdk/src/components/LoginForm.tsx +67 -0
- package/packages/react-sdk/src/components/OTPVerification.tsx +104 -0
- package/packages/react-sdk/src/components/SessionList.tsx +64 -0
- package/packages/react-sdk/src/components/SignupForm.tsx +95 -0
- package/packages/react-sdk/src/context/AuthContext.ts +4 -0
- package/packages/react-sdk/src/context/SecurePoolProvider.tsx +492 -0
- package/packages/react-sdk/src/hooks/useAuth.ts +11 -0
- package/packages/react-sdk/src/index.ts +22 -0
- package/packages/react-sdk/src/types.ts +53 -0
- package/packages/react-sdk/tsconfig.json +12 -0
- package/scripts/setup.js +285 -0
- package/scripts/setup.sh +309 -0
- package/tsconfig.base.json +16 -0
- package/turbo.json +16 -0
package/scripts/setup.js
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SecurePool - Cross-platform Setup Script
|
|
5
|
+
* Works on macOS, Linux, and Windows
|
|
6
|
+
*
|
|
7
|
+
* Usage: node scripts/setup.js
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync } = require("child_process");
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const path = require("path");
|
|
13
|
+
const readline = require("readline");
|
|
14
|
+
|
|
15
|
+
const ROOT = path.resolve(__dirname, "..");
|
|
16
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
17
|
+
|
|
18
|
+
function ask(question, defaultVal) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
const suffix = defaultVal ? ` [${defaultVal}]` : "";
|
|
21
|
+
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
22
|
+
resolve(answer.trim() || defaultVal || "");
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function run(cmd, opts = {}) {
|
|
28
|
+
try {
|
|
29
|
+
return execSync(cmd, { cwd: ROOT, stdio: opts.silent ? "pipe" : "inherit", ...opts }).toString().trim();
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function runSilent(cmd) {
|
|
36
|
+
return run(cmd, { silent: true, stdio: "pipe" });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const GREEN = "\x1b[32m";
|
|
40
|
+
const YELLOW = "\x1b[33m";
|
|
41
|
+
const RED = "\x1b[31m";
|
|
42
|
+
const BLUE = "\x1b[34m";
|
|
43
|
+
const BOLD = "\x1b[1m";
|
|
44
|
+
const NC = "\x1b[0m";
|
|
45
|
+
|
|
46
|
+
function step(num, total, msg) {
|
|
47
|
+
console.log(`\n${BLUE}${BOLD}[${num}/${total}]${NC} ${GREEN}${msg}${NC}`);
|
|
48
|
+
console.log("─".repeat(40));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function ok(msg) { console.log(` ${GREEN}✓${NC} ${msg}`); }
|
|
52
|
+
function warn(msg) { console.log(` ${YELLOW}⚠${NC} ${msg}`); }
|
|
53
|
+
function fail(msg) { console.log(` ${RED}✗${NC} ${msg}`); }
|
|
54
|
+
|
|
55
|
+
const TOTAL_STEPS = 7;
|
|
56
|
+
|
|
57
|
+
async function main() {
|
|
58
|
+
console.log(`\n${BOLD}═══════════════════════════════════════${NC}`);
|
|
59
|
+
console.log(`${BOLD} SecurePool Setup${NC}`);
|
|
60
|
+
console.log(`${BOLD}═══════════════════════════════════════${NC}`);
|
|
61
|
+
|
|
62
|
+
// ──────────────────────────────────────
|
|
63
|
+
// Step 1: Check Prerequisites
|
|
64
|
+
// ──────────────────────────────────────
|
|
65
|
+
step(1, TOTAL_STEPS, "Checking prerequisites");
|
|
66
|
+
|
|
67
|
+
const nodeV = runSilent("node -v");
|
|
68
|
+
if (nodeV) {
|
|
69
|
+
ok(`Node.js ${nodeV}`);
|
|
70
|
+
} else {
|
|
71
|
+
fail("Node.js not found. Install from https://nodejs.org");
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const npmV = runSilent("npm -v");
|
|
76
|
+
if (npmV) ok(`npm ${npmV}`);
|
|
77
|
+
|
|
78
|
+
const hasOpenSSL = runSilent("openssl version");
|
|
79
|
+
if (hasOpenSSL) {
|
|
80
|
+
ok("OpenSSL available");
|
|
81
|
+
} else {
|
|
82
|
+
warn("OpenSSL not found - you'll need to generate RSA keys manually");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const hasMongo = runSilent("mongosh --version");
|
|
86
|
+
if (hasMongo) {
|
|
87
|
+
ok(`mongosh ${hasMongo}`);
|
|
88
|
+
} else {
|
|
89
|
+
warn("mongosh not found - MongoDB setup will be skipped");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ──────────────────────────────────────
|
|
93
|
+
// Step 2: Install Dependencies
|
|
94
|
+
// ──────────────────────────────────────
|
|
95
|
+
step(2, TOTAL_STEPS, "Installing dependencies");
|
|
96
|
+
run("npm install");
|
|
97
|
+
ok("Dependencies installed");
|
|
98
|
+
|
|
99
|
+
// ──────────────────────────────────────
|
|
100
|
+
// Step 3: Generate RSA Keys
|
|
101
|
+
// ──────────────────────────────────────
|
|
102
|
+
step(3, TOTAL_STEPS, "Setting up JWT RSA keys");
|
|
103
|
+
|
|
104
|
+
const privPath = path.join(ROOT, "private.pem");
|
|
105
|
+
const pubPath = path.join(ROOT, "public.pem");
|
|
106
|
+
|
|
107
|
+
if (fs.existsSync(privPath) && fs.existsSync(pubPath)) {
|
|
108
|
+
ok("RSA keys already exist (private.pem, public.pem)");
|
|
109
|
+
} else if (hasOpenSSL) {
|
|
110
|
+
runSilent(`openssl genrsa -out "${privPath}" 2048`);
|
|
111
|
+
runSilent(`openssl rsa -in "${privPath}" -pubout -out "${pubPath}"`);
|
|
112
|
+
ok("Generated RSA key pair");
|
|
113
|
+
} else {
|
|
114
|
+
warn("Cannot generate keys without OpenSSL.");
|
|
115
|
+
warn("Generate manually:");
|
|
116
|
+
console.log(" openssl genrsa -out private.pem 2048");
|
|
117
|
+
console.log(" openssl rsa -in private.pem -pubout -out public.pem");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ──────────────────────────────────────
|
|
121
|
+
// Step 4: MongoDB Setup
|
|
122
|
+
// ──────────────────────────────────────
|
|
123
|
+
step(4, TOTAL_STEPS, "Database configuration");
|
|
124
|
+
|
|
125
|
+
let dbUrl = "mongodb://localhost:27017/securepool";
|
|
126
|
+
|
|
127
|
+
// Check if MongoDB is running
|
|
128
|
+
let mongoRunning = false;
|
|
129
|
+
if (hasMongo) {
|
|
130
|
+
const ping = runSilent('mongosh --eval "db.runCommand({ping:1})" --quiet 2>/dev/null');
|
|
131
|
+
if (ping && ping.includes("ok")) {
|
|
132
|
+
mongoRunning = true;
|
|
133
|
+
ok("MongoDB is running");
|
|
134
|
+
} else {
|
|
135
|
+
warn("MongoDB is not running. Start it before running the app.");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
console.log("");
|
|
140
|
+
console.log(` ${YELLOW}Choose database:${NC}`);
|
|
141
|
+
console.log(" 1) MongoDB without auth (simplest)");
|
|
142
|
+
console.log(" 2) MongoDB with auth");
|
|
143
|
+
console.log(" 3) Custom connection string");
|
|
144
|
+
console.log("");
|
|
145
|
+
|
|
146
|
+
const dbChoice = await ask("Enter choice", "1");
|
|
147
|
+
|
|
148
|
+
if (dbChoice === "2") {
|
|
149
|
+
const dbUser = await ask("DB username", "securepool-user");
|
|
150
|
+
const dbPass = await ask("DB password", "SecurePool@123");
|
|
151
|
+
const dbPassEncoded = dbPass.replace(/@/g, "%40");
|
|
152
|
+
dbUrl = `mongodb://${dbUser}:${dbPassEncoded}@localhost:27017/securepool?authSource=securepool`;
|
|
153
|
+
|
|
154
|
+
if (mongoRunning) {
|
|
155
|
+
const createUserCmd = `mongosh --quiet --eval "use securepool; try { db.createUser({ user: '${dbUser}', pwd: '${dbPass}', roles: [{ role: 'readWrite', db: 'securepool' }] }); print('created'); } catch(e) { print('exists'); }"`;
|
|
156
|
+
const result = runSilent(createUserCmd);
|
|
157
|
+
if (result && result.includes("created")) {
|
|
158
|
+
ok(`MongoDB user '${dbUser}' created`);
|
|
159
|
+
} else {
|
|
160
|
+
ok(`MongoDB user '${dbUser}' already exists`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} else if (dbChoice === "3") {
|
|
164
|
+
dbUrl = await ask("Connection string", dbUrl);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
ok(`Database: ${dbUrl.replace(/:[^:@]+@/, ":***@")}`);
|
|
168
|
+
|
|
169
|
+
// ──────────────────────────────────────
|
|
170
|
+
// Step 5: Email Setup
|
|
171
|
+
// ──────────────────────────────────────
|
|
172
|
+
step(5, TOTAL_STEPS, "Email configuration (for OTP)");
|
|
173
|
+
|
|
174
|
+
let emailHost = "";
|
|
175
|
+
let emailPort = "";
|
|
176
|
+
let emailSecure = "";
|
|
177
|
+
let emailUser = "";
|
|
178
|
+
let emailPass = "";
|
|
179
|
+
let emailFrom = "";
|
|
180
|
+
|
|
181
|
+
console.log("");
|
|
182
|
+
console.log(` ${YELLOW}Email is needed to send OTP codes.${NC}`);
|
|
183
|
+
console.log(" You can skip and add it later to .env");
|
|
184
|
+
console.log("");
|
|
185
|
+
|
|
186
|
+
const setupEmail = await ask("Setup email now? (y/N)", "N");
|
|
187
|
+
|
|
188
|
+
if (setupEmail.toLowerCase() === "y") {
|
|
189
|
+
console.log("");
|
|
190
|
+
console.log(" For Gmail: create App Password at");
|
|
191
|
+
console.log(" https://myaccount.google.com/apppasswords");
|
|
192
|
+
console.log("");
|
|
193
|
+
emailUser = await ask("Gmail address", "");
|
|
194
|
+
emailPass = await ask("App Password (16 chars)", "");
|
|
195
|
+
|
|
196
|
+
if (emailUser && emailPass) {
|
|
197
|
+
emailHost = "smtp.gmail.com";
|
|
198
|
+
emailPort = "587";
|
|
199
|
+
emailSecure = "false";
|
|
200
|
+
emailFrom = emailUser;
|
|
201
|
+
ok(`Email: ${emailUser}`);
|
|
202
|
+
} else {
|
|
203
|
+
warn("Email skipped (empty input)");
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
warn("Email skipped - OTP emails won't be sent");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ──────────────────────────────────────
|
|
210
|
+
// Step 6: Create .env
|
|
211
|
+
// ──────────────────────────────────────
|
|
212
|
+
step(6, TOTAL_STEPS, "Creating .env file");
|
|
213
|
+
|
|
214
|
+
const envPath = path.join(ROOT, ".env");
|
|
215
|
+
let writeEnv = true;
|
|
216
|
+
|
|
217
|
+
if (fs.existsSync(envPath)) {
|
|
218
|
+
const overwrite = await ask(".env already exists. Overwrite? (y/N)", "N");
|
|
219
|
+
writeEnv = overwrite.toLowerCase() === "y";
|
|
220
|
+
if (!writeEnv) warn("Keeping existing .env");
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (writeEnv) {
|
|
224
|
+
const envContent = `# Database
|
|
225
|
+
DB_TYPE=mongo
|
|
226
|
+
DB_URL=${dbUrl}
|
|
227
|
+
|
|
228
|
+
# JWT
|
|
229
|
+
JWT_PRIVATE_KEY_PATH=./private.pem
|
|
230
|
+
JWT_PUBLIC_KEY_PATH=./public.pem
|
|
231
|
+
|
|
232
|
+
# Email (Gmail SMTP)
|
|
233
|
+
EMAIL_HOST=${emailHost}
|
|
234
|
+
EMAIL_PORT=${emailPort}
|
|
235
|
+
EMAIL_SECURE=${emailSecure}
|
|
236
|
+
EMAIL_USER=${emailUser}
|
|
237
|
+
EMAIL_PASS=${emailPass}
|
|
238
|
+
EMAIL_FROM=${emailFrom}
|
|
239
|
+
|
|
240
|
+
# Server
|
|
241
|
+
PORT=5001
|
|
242
|
+
|
|
243
|
+
# Security
|
|
244
|
+
RATE_LIMIT_ENABLED=true
|
|
245
|
+
CORS_ORIGINS=*
|
|
246
|
+
`;
|
|
247
|
+
fs.writeFileSync(envPath, envContent);
|
|
248
|
+
ok(".env file created");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ──────────────────────────────────────
|
|
252
|
+
// Step 7: Build
|
|
253
|
+
// ──────────────────────────────────────
|
|
254
|
+
step(7, TOTAL_STEPS, "Building all packages");
|
|
255
|
+
run("npx turbo run build --filter=@securepool/*");
|
|
256
|
+
ok("All packages built");
|
|
257
|
+
|
|
258
|
+
// ──────────────────────────────────────
|
|
259
|
+
// Done
|
|
260
|
+
// ──────────────────────────────────────
|
|
261
|
+
console.log("");
|
|
262
|
+
console.log(`${BOLD}═══════════════════════════════════════${NC}`);
|
|
263
|
+
console.log(`${GREEN}${BOLD} SecurePool setup complete!${NC}`);
|
|
264
|
+
console.log(`${BOLD}═══════════════════════════════════════${NC}`);
|
|
265
|
+
console.log("");
|
|
266
|
+
console.log(` ${BOLD}Start backend:${NC}`);
|
|
267
|
+
console.log(" npm run start:backend");
|
|
268
|
+
console.log("");
|
|
269
|
+
console.log(` ${BOLD}Start frontend:${NC} (new terminal)`);
|
|
270
|
+
console.log(" npm run start:frontend");
|
|
271
|
+
console.log("");
|
|
272
|
+
console.log(` ${BOLD}Open:${NC}`);
|
|
273
|
+
console.log(" Frontend → http://localhost:5173");
|
|
274
|
+
console.log(" API Docs → http://localhost:5001/docs");
|
|
275
|
+
console.log(" Health → http://localhost:5001/health");
|
|
276
|
+
console.log("");
|
|
277
|
+
|
|
278
|
+
rl.close();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
main().catch((err) => {
|
|
282
|
+
console.error(err);
|
|
283
|
+
rl.close();
|
|
284
|
+
process.exit(1);
|
|
285
|
+
});
|
package/scripts/setup.sh
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# =============================================
|
|
4
|
+
# SecurePool - One-Command Setup Script
|
|
5
|
+
# =============================================
|
|
6
|
+
|
|
7
|
+
set -e
|
|
8
|
+
|
|
9
|
+
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
10
|
+
cd "$ROOT_DIR"
|
|
11
|
+
|
|
12
|
+
# Colors
|
|
13
|
+
GREEN='\033[0;32m'
|
|
14
|
+
YELLOW='\033[1;33m'
|
|
15
|
+
RED='\033[0;31m'
|
|
16
|
+
BLUE='\033[0;34m'
|
|
17
|
+
NC='\033[0m' # No Color
|
|
18
|
+
BOLD='\033[1m'
|
|
19
|
+
|
|
20
|
+
print_step() {
|
|
21
|
+
echo ""
|
|
22
|
+
echo -e "${BLUE}${BOLD}[$1/8]${NC} ${GREEN}$2${NC}"
|
|
23
|
+
echo "─────────────────────────────────────"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
print_success() {
|
|
27
|
+
echo -e " ${GREEN}✓${NC} $1"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
print_warn() {
|
|
31
|
+
echo -e " ${YELLOW}⚠${NC} $1"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
print_error() {
|
|
35
|
+
echo -e " ${RED}✗${NC} $1"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# =============================================
|
|
39
|
+
# Step 1: Check Prerequisites
|
|
40
|
+
# =============================================
|
|
41
|
+
print_step 1 "Checking prerequisites"
|
|
42
|
+
|
|
43
|
+
# Node.js
|
|
44
|
+
if command -v node &> /dev/null; then
|
|
45
|
+
NODE_VERSION=$(node -v)
|
|
46
|
+
print_success "Node.js $NODE_VERSION"
|
|
47
|
+
else
|
|
48
|
+
print_error "Node.js not found. Install with: brew install node"
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# npm
|
|
53
|
+
if command -v npm &> /dev/null; then
|
|
54
|
+
NPM_VERSION=$(npm -v)
|
|
55
|
+
print_success "npm $NPM_VERSION"
|
|
56
|
+
else
|
|
57
|
+
print_error "npm not found"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
# OpenSSL
|
|
62
|
+
if command -v openssl &> /dev/null; then
|
|
63
|
+
print_success "OpenSSL available"
|
|
64
|
+
else
|
|
65
|
+
print_error "OpenSSL not found"
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# MongoDB
|
|
70
|
+
if command -v mongosh &> /dev/null; then
|
|
71
|
+
print_success "mongosh available"
|
|
72
|
+
else
|
|
73
|
+
print_warn "mongosh not found. Install with: brew install mongodb-community"
|
|
74
|
+
print_warn "Skipping MongoDB setup - you'll need to configure DB_URL manually"
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# =============================================
|
|
78
|
+
# Step 2: Install Dependencies
|
|
79
|
+
# =============================================
|
|
80
|
+
print_step 2 "Installing dependencies"
|
|
81
|
+
|
|
82
|
+
npm install --silent 2>&1 | tail -1
|
|
83
|
+
print_success "All packages installed"
|
|
84
|
+
|
|
85
|
+
# =============================================
|
|
86
|
+
# Step 3: Generate RSA Keys
|
|
87
|
+
# =============================================
|
|
88
|
+
print_step 3 "Setting up JWT RSA keys"
|
|
89
|
+
|
|
90
|
+
if [ -f "$ROOT_DIR/private.pem" ] && [ -f "$ROOT_DIR/public.pem" ]; then
|
|
91
|
+
print_success "RSA keys already exist (private.pem, public.pem)"
|
|
92
|
+
else
|
|
93
|
+
openssl genrsa -out "$ROOT_DIR/private.pem" 2048 2>/dev/null
|
|
94
|
+
openssl rsa -in "$ROOT_DIR/private.pem" -pubout -out "$ROOT_DIR/public.pem" 2>/dev/null
|
|
95
|
+
print_success "Generated new RSA key pair"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# =============================================
|
|
99
|
+
# Step 4: Setup MongoDB
|
|
100
|
+
# =============================================
|
|
101
|
+
print_step 4 "Setting up MongoDB"
|
|
102
|
+
|
|
103
|
+
MONGO_RUNNING=false
|
|
104
|
+
DB_URL=""
|
|
105
|
+
|
|
106
|
+
# Check if MongoDB is running
|
|
107
|
+
if mongosh --eval "db.runCommand({ ping: 1 })" --quiet 2>/dev/null | grep -q "ok"; then
|
|
108
|
+
MONGO_RUNNING=true
|
|
109
|
+
print_success "MongoDB is running"
|
|
110
|
+
else
|
|
111
|
+
# Try to start MongoDB
|
|
112
|
+
if command -v brew &> /dev/null; then
|
|
113
|
+
print_warn "MongoDB not running. Attempting to start..."
|
|
114
|
+
brew services start mongodb-community 2>/dev/null || true
|
|
115
|
+
sleep 2
|
|
116
|
+
if mongosh --eval "db.runCommand({ ping: 1 })" --quiet 2>/dev/null | grep -q "ok"; then
|
|
117
|
+
MONGO_RUNNING=true
|
|
118
|
+
print_success "MongoDB started via brew"
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
if [ "$MONGO_RUNNING" = true ]; then
|
|
124
|
+
# Ask about authentication setup
|
|
125
|
+
echo ""
|
|
126
|
+
echo -e " ${YELLOW}Choose MongoDB setup:${NC}"
|
|
127
|
+
echo " 1) Simple (no auth) - recommended for quick start"
|
|
128
|
+
echo " 2) With authentication - production-like setup"
|
|
129
|
+
echo ""
|
|
130
|
+
read -p " Enter choice [1]: " MONGO_CHOICE
|
|
131
|
+
MONGO_CHOICE=${MONGO_CHOICE:-1}
|
|
132
|
+
|
|
133
|
+
if [ "$MONGO_CHOICE" = "2" ]; then
|
|
134
|
+
echo ""
|
|
135
|
+
read -p " DB username [securepool-user]: " DB_USER
|
|
136
|
+
DB_USER=${DB_USER:-securepool-user}
|
|
137
|
+
read -p " DB password [SecurePool@123]: " DB_PASS
|
|
138
|
+
DB_PASS=${DB_PASS:-SecurePool@123}
|
|
139
|
+
DB_NAME="securepool"
|
|
140
|
+
|
|
141
|
+
# Create user in MongoDB
|
|
142
|
+
mongosh --quiet --eval "
|
|
143
|
+
use $DB_NAME;
|
|
144
|
+
try {
|
|
145
|
+
db.createUser({
|
|
146
|
+
user: '$DB_USER',
|
|
147
|
+
pwd: '$DB_PASS',
|
|
148
|
+
roles: [{ role: 'readWrite', db: '$DB_NAME' }]
|
|
149
|
+
});
|
|
150
|
+
print('User created successfully');
|
|
151
|
+
} catch(e) {
|
|
152
|
+
if (e.codeName === 'DuplicateKey' || e.code === 11000) {
|
|
153
|
+
print('User already exists - skipping');
|
|
154
|
+
} else {
|
|
155
|
+
print('Note: ' + e.message);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
" 2>/dev/null || true
|
|
159
|
+
|
|
160
|
+
# URL-encode the password (replace @ with %40)
|
|
161
|
+
DB_PASS_ENCODED=$(echo "$DB_PASS" | sed 's/@/%40/g')
|
|
162
|
+
DB_URL="mongodb://${DB_USER}:${DB_PASS_ENCODED}@localhost:27017/${DB_NAME}?authSource=${DB_NAME}"
|
|
163
|
+
print_success "MongoDB user '${DB_USER}' configured"
|
|
164
|
+
else
|
|
165
|
+
DB_URL="mongodb://localhost:27017/securepool"
|
|
166
|
+
print_success "Using MongoDB without authentication"
|
|
167
|
+
fi
|
|
168
|
+
else
|
|
169
|
+
print_warn "MongoDB not available. Using default URL (update .env manually)"
|
|
170
|
+
DB_URL="mongodb://localhost:27017/securepool"
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# =============================================
|
|
174
|
+
# Step 5: Setup Email (Optional)
|
|
175
|
+
# =============================================
|
|
176
|
+
print_step 5 "Email configuration (for OTP)"
|
|
177
|
+
|
|
178
|
+
echo ""
|
|
179
|
+
echo -e " ${YELLOW}Email is needed for OTP verification.${NC}"
|
|
180
|
+
echo " You can skip this and add it later to .env"
|
|
181
|
+
echo ""
|
|
182
|
+
read -p " Setup email now? [y/N]: " SETUP_EMAIL
|
|
183
|
+
SETUP_EMAIL=${SETUP_EMAIL:-N}
|
|
184
|
+
|
|
185
|
+
EMAIL_HOST=""
|
|
186
|
+
EMAIL_PORT=""
|
|
187
|
+
EMAIL_SECURE=""
|
|
188
|
+
EMAIL_USER=""
|
|
189
|
+
EMAIL_PASS=""
|
|
190
|
+
EMAIL_FROM=""
|
|
191
|
+
|
|
192
|
+
if [[ "$SETUP_EMAIL" =~ ^[Yy]$ ]]; then
|
|
193
|
+
echo ""
|
|
194
|
+
echo " Using Gmail SMTP (most common):"
|
|
195
|
+
echo " 1. Go to https://myaccount.google.com/apppasswords"
|
|
196
|
+
echo " 2. Generate an App Password"
|
|
197
|
+
echo ""
|
|
198
|
+
read -p " Gmail address: " EMAIL_USER
|
|
199
|
+
read -p " App Password (16 chars): " EMAIL_PASS
|
|
200
|
+
|
|
201
|
+
if [ -n "$EMAIL_USER" ] && [ -n "$EMAIL_PASS" ]; then
|
|
202
|
+
EMAIL_HOST="smtp.gmail.com"
|
|
203
|
+
EMAIL_PORT="587"
|
|
204
|
+
EMAIL_SECURE="false"
|
|
205
|
+
EMAIL_FROM="$EMAIL_USER"
|
|
206
|
+
print_success "Email configured: $EMAIL_USER"
|
|
207
|
+
else
|
|
208
|
+
print_warn "Email skipped (empty input)"
|
|
209
|
+
fi
|
|
210
|
+
else
|
|
211
|
+
print_warn "Email skipped - OTP emails won't be sent"
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# =============================================
|
|
215
|
+
# Step 6: Create .env File
|
|
216
|
+
# =============================================
|
|
217
|
+
print_step 6 "Creating .env file"
|
|
218
|
+
|
|
219
|
+
ENV_FILE="$ROOT_DIR/.env"
|
|
220
|
+
|
|
221
|
+
if [ -f "$ENV_FILE" ]; then
|
|
222
|
+
read -p " .env already exists. Overwrite? [y/N]: " OVERWRITE
|
|
223
|
+
OVERWRITE=${OVERWRITE:-N}
|
|
224
|
+
if [[ ! "$OVERWRITE" =~ ^[Yy]$ ]]; then
|
|
225
|
+
print_warn "Keeping existing .env file"
|
|
226
|
+
else
|
|
227
|
+
WRITE_ENV=true
|
|
228
|
+
fi
|
|
229
|
+
else
|
|
230
|
+
WRITE_ENV=true
|
|
231
|
+
fi
|
|
232
|
+
|
|
233
|
+
if [ "$WRITE_ENV" = true ]; then
|
|
234
|
+
cat > "$ENV_FILE" << ENVEOF
|
|
235
|
+
# Database
|
|
236
|
+
DB_TYPE=mongo
|
|
237
|
+
DB_URL=$DB_URL
|
|
238
|
+
|
|
239
|
+
# JWT
|
|
240
|
+
JWT_PRIVATE_KEY_PATH=./private.pem
|
|
241
|
+
JWT_PUBLIC_KEY_PATH=./public.pem
|
|
242
|
+
|
|
243
|
+
# Email (Gmail SMTP)
|
|
244
|
+
EMAIL_HOST=$EMAIL_HOST
|
|
245
|
+
EMAIL_PORT=$EMAIL_PORT
|
|
246
|
+
EMAIL_SECURE=$EMAIL_SECURE
|
|
247
|
+
EMAIL_USER=$EMAIL_USER
|
|
248
|
+
EMAIL_PASS=$EMAIL_PASS
|
|
249
|
+
EMAIL_FROM=$EMAIL_FROM
|
|
250
|
+
|
|
251
|
+
# Server
|
|
252
|
+
PORT=5001
|
|
253
|
+
|
|
254
|
+
# Security
|
|
255
|
+
RATE_LIMIT_ENABLED=true
|
|
256
|
+
CORS_ORIGINS=*
|
|
257
|
+
ENVEOF
|
|
258
|
+
print_success ".env file created"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
# =============================================
|
|
262
|
+
# Step 7: Build All Packages
|
|
263
|
+
# =============================================
|
|
264
|
+
print_step 7 "Building all packages"
|
|
265
|
+
|
|
266
|
+
npx turbo run build --filter='@securepool/*' 2>&1 | grep -E "(successful|Failed|cached)" | tail -1
|
|
267
|
+
print_success "All packages built"
|
|
268
|
+
|
|
269
|
+
# =============================================
|
|
270
|
+
# Step 8: Verify Setup
|
|
271
|
+
# =============================================
|
|
272
|
+
print_step 8 "Verifying setup"
|
|
273
|
+
|
|
274
|
+
# Check all dist folders exist
|
|
275
|
+
PACKAGES_OK=true
|
|
276
|
+
for pkg in core application infrastructure persistence api react-sdk; do
|
|
277
|
+
if [ -d "$ROOT_DIR/packages/$pkg/dist" ]; then
|
|
278
|
+
print_success "@securepool/$pkg built"
|
|
279
|
+
else
|
|
280
|
+
print_error "@securepool/$pkg - dist missing!"
|
|
281
|
+
PACKAGES_OK=false
|
|
282
|
+
fi
|
|
283
|
+
done
|
|
284
|
+
|
|
285
|
+
# =============================================
|
|
286
|
+
# Done!
|
|
287
|
+
# =============================================
|
|
288
|
+
echo ""
|
|
289
|
+
echo "═══════════════════════════════════════════"
|
|
290
|
+
echo -e "${GREEN}${BOLD} SecurePool setup complete!${NC}"
|
|
291
|
+
echo "═══════════════════════════════════════════"
|
|
292
|
+
echo ""
|
|
293
|
+
echo -e " ${BOLD}Start backend:${NC}"
|
|
294
|
+
echo " cd apps/demo-backend"
|
|
295
|
+
echo " npx ts-node src/index.ts"
|
|
296
|
+
echo ""
|
|
297
|
+
echo -e " ${BOLD}Start frontend:${NC} (new terminal)"
|
|
298
|
+
echo " cd apps/demo-frontend"
|
|
299
|
+
echo " npx vite"
|
|
300
|
+
echo ""
|
|
301
|
+
echo -e " ${BOLD}API Docs:${NC}"
|
|
302
|
+
echo " http://localhost:5001/docs"
|
|
303
|
+
echo ""
|
|
304
|
+
echo -e " ${BOLD}Frontend:${NC}"
|
|
305
|
+
echo " http://localhost:5173"
|
|
306
|
+
echo ""
|
|
307
|
+
echo -e " ${BOLD}Health check:${NC}"
|
|
308
|
+
echo " curl http://localhost:5001/health"
|
|
309
|
+
echo ""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"sourceMap": true,
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"moduleResolution": "node"
|
|
15
|
+
}
|
|
16
|
+
}
|
package/turbo.json
ADDED