Browse Source

- Update the server to allow for users

master
Quildra 1 year ago
parent
commit
9f3415155e
  1. 1
      .gitignore
  2. 866
      origin-dex-api/package-lock.json
  3. 5
      origin-dex-api/package.json
  4. 251
      origin-dex-api/src/server.ts
  5. 4
      origin-dex/src/app/app.routes.ts
  6. 1
      origin-dex/src/app/features/plan/plan.component.ts
  7. 1
      origin-dex/src/index.html
  8. 19
      origin-dex/src/styles.scss

1
.gitignore

@ -8,3 +8,4 @@ ui_feedback.log
logs/qt_log.log
logs/default.log
node_modules/
.env

866
origin-dex-api/package-lock.json

File diff suppressed because it is too large

5
origin-dex-api/package.json

@ -13,8 +13,13 @@
"license": "ISC",
"description": "",
"dependencies": {
"@types/bcrypt": "^5.0.2",
"@types/jsonwebtoken": "^9.0.7",
"bcrypt": "^5.1.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.21.1",
"jsonwebtoken": "^9.0.2",
"sqlite": "^5.0.1",
"sqlite3": "^5.1.6"
},

251
origin-dex-api/src/server.ts

@ -1,15 +1,141 @@
import dotenv from 'dotenv';
// Add this line before other imports
dotenv.config();
import express from 'express';
import { Request, Response, NextFunction } from 'express';
import cors from 'cors';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import fs from 'fs/promises';
import path from 'path';
const app = express();
const port = process.env.PORT || 5000;
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) {
throw new Error('JWT_SECRET must be defined');
}
interface AuthRequest extends Request {
user?: any;
}
// Middleware
app.use(cors());
app.use(express.json());
const userDbPromise = open({
filename: './user_data.db', // This will be created in the api folder
driver: sqlite3.Database
});
// Initialize users table
async function initializeDb() {
const db = await userDbPromise;
await db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS caught_pokemon (
user_id INTEGER,
pfic TEXT,
caught_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (user_id, pfic),
FOREIGN KEY (user_id) REFERENCES users(id)
);
`);
}
initializeDb().catch(console.error);
// Update the auth middleware
const authenticateToken = (
req: AuthRequest,
res: Response,
next: NextFunction
): void => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
res.status(401).json({ error: 'Authentication required' });
return;
}
try {
const user = jwt.verify(token, JWT_SECRET as jwt.Secret);
req.user = user;
next();
} catch (err) {
res.status(403).json({ error: 'Invalid token' });
return;
}
};
// Update the route handlers
app.post('/api/auth/register', (req: Request, res: Response) => {
const { username, password } = req.body;
userDbPromise.then(async (db) => {
try {
const existingUser = await db.get('SELECT id FROM users WHERE username = ?', username);
if (existingUser) {
return res.status(400).json({ error: 'Username already exists' });
}
const hashedPassword = await bcrypt.hash(password, 10);
await db.run(
'INSERT INTO users (username, password) VALUES (?, ?)',
[username, hashedPassword]
);
res.status(201).json({ message: 'User registered successfully' });
} catch (err) {
console.error('Registration error:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
});
app.post('/api/auth/login', (req: Request, res: Response) => {
const { username, password } = req.body;
userDbPromise.then(async (db) => {
try {
const user = await db.get('SELECT * FROM users WHERE username = ?', username);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const validPassword = await bcrypt.compare(password, user.password);
if (!validPassword) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jwt.sign(
{ id: user.id, username: user.username },
JWT_SECRET as jwt.Secret,
{ expiresIn: '24h' }
);
res.json({
id: user.id,
username: user.username,
token
});
} catch (err) {
console.error('Login error:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
});
// Database connection
const dbPromise = open({
filename: '../pokemon_forms.db', // Adjust path to your database file
@ -84,8 +210,8 @@ app.get('/api/pokemon', async (req, res) => {
ps.storable_in_home, m.icon_path, m.name as mark_name
FROM pokemon_forms pf
JOIN pokemon_storage ps ON pf.PFIC = ps.PFIC
JOIN form_marks fm ON pf.PFIC = fm.pfic
JOIN marks m ON fm.mark_id = m.id
LEFT JOIN form_marks fm ON pf.PFIC = fm.pfic
LEFT JOIN marks m ON fm.mark_id = m.id
WHERE ps.storable_in_home = 1
ORDER BY pf.PFIC
`);
@ -128,14 +254,125 @@ app.get('/api/pokemon/:pfic/details', async (req, res) => {
}
});
app.get('/api/pokemon/caught', async (req, res) => {
// This will need to be implemented with your user authentication system
res.json([]);
app.get('/api/plan', async (req: Request, res: Response) => {
try {
// Read the efficiency plan file
const planData = await fs.readFile(
path.join(__dirname, '../../efficiency_plan.json'),
'utf-8'
);
const efficiencyPlan = JSON.parse(planData);
// Get the Pokemon database connection
const db = await open({
filename: '../pokemon_forms.db',
driver: sqlite3.Database
});
app.post('/api/pokemon/caught/:pfic', async (req, res) => {
// This will need to be implemented with your user authentication system
// Helper function to get evolution methods
async function getEvolutionMethods(fromPfic: string, toPfic: string) {
// Try direct evolution first
const direct = await db.get(`
SELECT method, to_pfic
FROM evolution_chains
WHERE from_pfic = ? AND to_pfic = ?
`, [fromPfic, toPfic]);
if (direct) {
return [direct.method];
}
// Try indirect evolution path
const methods = await db.all(`
WITH RECURSIVE evolution_path AS (
SELECT from_pfic, to_pfic, method, 1 as depth
FROM evolution_chains
WHERE from_pfic = ?
UNION ALL
SELECT e.from_pfic, e.to_pfic, e.method, ep.depth + 1
FROM evolution_chains e
JOIN evolution_path ep ON e.from_pfic = ep.to_pfic
WHERE ep.depth < 3
)
SELECT method
FROM evolution_path
WHERE to_pfic = ?
ORDER BY depth;
`, [fromPfic, toPfic]);
if (methods && methods.length > 0) {
return methods.map(m => m.method);
}
return ['Evolution'];
}
// Enhance the plan with evolution methods
for (const game of efficiencyPlan) {
for (const pokemon of game.pokemon) {
if (pokemon.evolve_to) {
for (const evolution of pokemon.evolve_to) {
const methods = await getEvolutionMethods(pokemon.pfic, evolution.pfic);
evolution.method = methods.join(' → ');
}
}
}
}
await db.close();
res.json(efficiencyPlan);
} catch (err) {
console.error('Error loading efficiency plan:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
// Update the caught Pokemon routes
app.get('/api/pokemon/caught', authenticateToken, (req: AuthRequest, res: Response) => {
void userDbPromise.then(async (db) => {
try {
const caught = await db.all(
'SELECT pfic FROM caught_pokemon WHERE user_id = ?',
req.user.id
);
res.json(caught.map(c => c.pfic));
} catch (err) {
console.error('Error fetching caught pokemon:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
});
app.post('/api/pokemon/caught/:pfic', authenticateToken, (req: AuthRequest, res: Response) => {
const { pfic } = req.params;
void userDbPromise.then(async (db) => {
try {
const existing = await db.get(
'SELECT 1 FROM caught_pokemon WHERE user_id = ? AND pfic = ?',
[req.user.id, pfic]
);
if (existing) {
await db.run(
'DELETE FROM caught_pokemon WHERE user_id = ? AND pfic = ?',
[req.user.id, pfic]
);
res.json({ status: 'released' });
} else {
await db.run(
'INSERT INTO caught_pokemon (user_id, pfic) VALUES (?, ?)',
[req.user.id, pfic]
);
res.json({ status: 'caught' });
}
} catch (err) {
console.error('Error updating caught status:', err);
res.status(500).json({ error: 'Internal server error' });
}
});
});
app.listen(port, () => {

4
origin-dex/src/app/app.routes.ts

@ -19,8 +19,8 @@ export const routes: Routes = [
},
{
path: 'efficiency',
loadComponent: () => import('./features/plan/plan-game/plan-game.component')
.then(m => m.PlanGameComponent),
loadComponent: () => import('./features/plan/plan.component')
.then(m => m.PlanComponent),
canActivate: [AuthGuard]
},
{

1
origin-dex/src/app/features/plan/plan.component.ts

@ -25,7 +25,6 @@ import { GamePlan } from '../../core/models/plan.model';
<app-plan-game
*ngFor="let game of gamePlans"
[game]="game"
(statusUpdate)="onPokemonStatusUpdate($event)"
></app-plan-game>
</div>
</div>

1
origin-dex/src/index.html

@ -6,6 +6,7 @@
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<app-root></app-root>

19
origin-dex/src/styles.scss

@ -1,5 +1,10 @@
@use '@angular/material' as mat;
@import "@angular/material/prebuilt-themes/indigo-pink.css";
@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
/* You can add global styles to this file, and also import other style files */
html, body {
height: 100%;
@ -9,3 +14,17 @@ body {
margin: 0;
font-family: Roboto, "Helvetica Neue", sans-serif;
}
.material-icons {
font-family: 'Material Icons';
font-weight: normal;
font-style: normal;
font-size: 24px; /* Preferred icon size */
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
}
Loading…
Cancel
Save