u
This commit is contained in:
66
server.js
66
server.js
@@ -5,6 +5,8 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
import webpack from 'webpack';
|
||||
import webpackDevMiddleware from 'webpack-dev-middleware';
|
||||
import bcrypt from 'bcrypt';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from './webpack.config.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
@@ -17,6 +19,7 @@ const USER_AGENT = 'ACController/1.9.7 (com.acinfinity.humiture; build:533; iOS
|
||||
const POLL_INTERVAL_MS = 60000; // 60 seconds
|
||||
const DB_FILE = 'ac_data.db';
|
||||
const PORT = 3905;
|
||||
const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-change-in-production';
|
||||
|
||||
// Device Type Mapping
|
||||
const DEVICE_TYPES = {
|
||||
@@ -52,11 +55,23 @@ db.exec(`
|
||||
)
|
||||
`);
|
||||
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username TEXT UNIQUE NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
role TEXT NOT NULL DEFAULT 'user',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`);
|
||||
|
||||
const insertStmt = db.prepare(`
|
||||
INSERT INTO readings (dev_id, dev_name, port, port_name, temp_c, humidity, vpd, fan_speed, on_speed, off_speed)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`);
|
||||
|
||||
const getUserByUsername = db.prepare('SELECT * FROM users WHERE username = ?');
|
||||
|
||||
// --- AC INFINITY API LOGIC ---
|
||||
let token = null;
|
||||
|
||||
@@ -201,7 +216,58 @@ async function poll() {
|
||||
|
||||
// --- EXPRESS SERVER ---
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
||||
// Auth: Login
|
||||
app.post('/api/auth/login', async (req, res) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
if (!username || !password) {
|
||||
return res.status(400).json({ error: 'Username and password required' });
|
||||
}
|
||||
|
||||
const user = getUserByUsername.get(username);
|
||||
if (!user) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
const valid = await bcrypt.compare(password, user.password_hash);
|
||||
if (!valid) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
const token = jwt.sign(
|
||||
{ id: user.id, username: user.username, role: user.role },
|
||||
JWT_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
res.json({ token, user: { username: user.username, role: user.role } });
|
||||
} catch (error) {
|
||||
console.error('Login error:', error);
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// Auth: Get current user
|
||||
app.get('/api/auth/me', (req, res) => {
|
||||
try {
|
||||
const authHeader = req.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
return res.status(401).json({ error: 'No token provided' });
|
||||
}
|
||||
|
||||
const token = authHeader.split(' ')[1];
|
||||
const decoded = jwt.verify(token, JWT_SECRET);
|
||||
|
||||
res.json({ user: { username: decoded.username, role: decoded.role } });
|
||||
} catch (error) {
|
||||
if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') {
|
||||
return res.status(401).json({ error: 'Invalid or expired token' });
|
||||
}
|
||||
res.status(500).json({ error: 'Internal server error' });
|
||||
}
|
||||
});
|
||||
|
||||
// API: Devices
|
||||
app.get('/api/devices', (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user