require('dotenv').config(); const express = require('express'); const session = require('express-session'); const expressLayouts = require('express-ejs-layouts'); const cookieParser = require('cookie-parser'); const RedisStore = require('connect-redis').default; const redis = require('redis'); const passport = require('passport'); const DiscordStrategy = require('passport-discord').Strategy; const { sequelize, User } = require('./models'); const setupWebSocketServer = require('./websocket'); const authRoutes = require('./routes/authRoutes'); const chatRoutes = require('./routes/chatRoutes'); const isotopeRoutes = require('./routes/isotopeRoutes'); const accountRoutes = require('./routes/accountRoutes'); const dashboardRoutes = require('./routes/dashboardRoutes'); const licenseRoutes = require('./routes/licenseRoutes'); const http = require('http'); const WebSocket = require('ws'); const app = express(); const server = http.createServer(app); const wss = new WebSocket.Server({ noServer: true }); // 必要な環境変数の確認 const requiredEnvVariables = [ 'REDIS_HOST', 'REDIS_PORT', 'REDIS_PASSWORD', 'SESSION_SECRET', 'SESSION_EXPIRATION', 'DISCORD_CLIENT_ID', 'DISCORD_CLIENT_SECRET', 'DISCORD_REDIRECT_URI' ]; requiredEnvVariables.forEach((envVar) => { if (!process.env[envVar]) { throw new Error(`環境変数 ${envVar} が設定されていません。`); } }); // Redisクライアントを作成 const redisClient = redis.createClient({ url: `redis://:${process.env.REDIS_PASSWORD}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}` }); redisClient.on('error', (err) => { console.log('Redis Client Error', err); process.exit(1); }); redisClient.connect().then(() => { console.log('Connected to Redis'); }); // Redis セッションストアの設定 const sessionMiddleware = session({ store: new RedisStore({ client: redisClient }), secret: process.env.SESSION_SECRET, resave: false, saveUninitialized: false, cookie: { maxAge: parseInt(process.env.SESSION_EXPIRATION) * 1000 } }); app.use(sessionMiddleware); // Cookie パーサーの設定 app.use(cookieParser()); // パスポートの初期化 app.use(passport.initialize()); app.use(passport.session()); // WebSocket と Express セッションの連携 server.on('upgrade', (request, socket, head) => { sessionMiddleware(request, {}, () => { wss.handleUpgrade(request, socket, head, (ws) => { wss.emit('connection', ws, request); }); }); }); // パスポートのDiscord戦略設定 passport.use(new DiscordStrategy({ clientID: process.env.DISCORD_CLIENT_ID, clientSecret: process.env.DISCORD_CLIENT_SECRET, callbackURL: process.env.DISCORD_REDIRECT_URI, scope: ['identify', 'email'] }, async (accessToken, refreshToken, profile, done) => { try { let user = await User.findOne({ where: { discordId: profile.id } }); if (user) { return done(null, user); } else { user = await User.create({ discordId: profile.id, username: profile.username, email: profile.email, isAccountSetupComplete: false }); return done(null, user); } } catch (err) { return done(err, null); } })); // シリアライズとデシリアライズ passport.serializeUser((user, done) => { done(null, user.id); }); passport.deserializeUser(async (id, done) => { try { const user = await User.findByPk(id); done(null, user); } catch (err) { done(err, null); } }); // JSON形式のリクエストボディをパースするミドルウェアを追加 app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(express.static('public')); app.set('view engine', 'ejs'); app.use(expressLayouts); app.set('layout', 'layout'); // ルート設定 app.use(authRoutes); app.use(chatRoutes); app.use(isotopeRoutes); app.use(accountRoutes); app.use(dashboardRoutes); app.use(licenseRoutes); app.get('/', (req, res) => { if (req.isAuthenticated()) { res.redirect('/dashboard'); } else { // layoutを使わずにindex.ejsだけを表示 res.render('index', { layout: false }); } }); // WebSocketサーバーのセットアップ setupWebSocketServer(wss); // サーバーの起動 (async () => { await sequelize.sync(); server.listen(3000, () => { console.log('サーバーがhttp://localhost:3000で起動しました。'); }); })();