好的, 我们来扩展这个招聘网站, 添加职位搜索、应用职位功能, 并实现用户认证, 包括 Google 和 GitHub 的 OAuth 2 登录。这个扩展版本会相对复杂一些, 我们将分步骤实现:

  1. 更新依赖:
bun add @elysiajs/cookie @elysiajs/jwt
bun add oauth4webapi
  1. 更新数据库结构:
CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  email TEXT UNIQUE NOT NULL,
  name TEXT,
  auth_provider TEXT NOT NULL
);
 
CREATE TABLE applications (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  job_id INTEGER NOT NULL,
  user_id INTEGER NOT NULL,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
  FOREIGN KEY (job_id) REFERENCES jobs(id),
  FOREIGN KEY (user_id) REFERENCES users(id)
);
 
-- Add an index to improve search performance
CREATE INDEX idx_jobs_title ON jobs(title);
  1. 更新 src/index. Ts:
import { Elysia, t } from 'elysia'
import { html } from '@elysiajs/html'
import { staticPlugin } from '@elysiajs/static'
import { cookie } from '@elysiajs/cookie'
import { jwt } from '@elysiajs/jwt'
import { Client } from '@libsql/client'
import * as oauth from 'oauth4webapi'
 
const db = new Client({
  url: 'YOUR_TURSO_DATABASE_URL',
  authToken: 'YOUR_TURSO_AUTH_TOKEN'
})
 
const app = new Elysia()
  .use(html())
  .use(staticPlugin())
  .use(cookie())
  .use(jwt({
    name: 'jwt',
    secret: 'your-jwt-secret'
  }))
 
  // OAuth2 configurations
  const googleConfig = {
    clientId: 'YOUR_GOOGLE_CLIENT_ID',
    clientSecret: 'YOUR_GOOGLE_CLIENT_SECRET',
    redirectUri: 'http://localhost:3000/auth/google/callback'
  }
 
  const githubConfig = {
    clientId: 'YOUR_GITHUB_CLIENT_ID',
    clientSecret: 'YOUR_GITHUB_CLIENT_SECRET',
    redirectUri: 'http://localhost:3000/auth/github/callback'
  }
 
  // Helper function to check if user is authenticated
  const isAuthenticated = async ({ jwt, cookie: { auth } }) => {
    if (!auth) return false
    try {
      await jwt.verify(auth)
      return true
    } catch {
      return false
    }
  }
 
  // Main page
  .get('/', async (context) => {
    const authenticated = await isAuthenticated(context)
    return `
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Job Board</title>
        <script src="/htmx.min.js"></script>
      </head>
      <body>
        <h1>Job Board</h1>
        ${authenticated ? `
          <button hx-post="/logout" hx-swap="outerHTML">Logout</button>
        ` : `
          <a href="/auth/google">Login with Google</a>
          <a href="/auth/github">Login with GitHub</a>
        `}
        <div>
          <input type="text" name="search" placeholder="Search jobs" hx-get="/jobs" hx-trigger="keyup changed delay:500ms" hx-target="#job-list">
        </div>
        <div id="job-list" hx-get="/jobs" hx-trigger="load"></div>
        ${authenticated ? `
          <button hx-get="/job-form" hx-target="#job-form">Post a Job</button>
          <div id="job-form"></div>
        ` : ''}
      </body>
      </html>
    `
  })
 
  // Job list with search
  .get('/jobs', async ({ query }) => {
    const search = query.search || ''
    const jobs = await db.execute({
      Sql: 'SELECT * FROM jobs WHERE title LIKE ? ORDER BY created_at DESC',
      Args: [`%${search}%`]
    })
    return jobs.rows.map(job => `
      <div>
        <h2>${job.title}</h2>
        <p>${job.company}</p>
        <p>${job.description}</p>
        <button hx-post="/apply/${job.id}" hx-swap="outerHTML">Apply</button>
      </div>
    `).join('')
  })
 
  // Job form
  .get('/job-form', () => `
    <form hx-post="/jobs" hx-target="#job-list">
      <input name="title" placeholder="Job Title" required>
      <input name="company" placeholder="Company" required>
      <textarea name="description" placeholder="Job Description" required></textarea>
      <button type="submit">Post Job</button>
    </form>
  `)
 
  // Post new job
  .post('/jobs', async ({ body }) => {
    const { title, company, description } = body
    await db.execute({
      sql: 'INSERT INTO jobs (title, company, description) VALUES (?, ?, ?)',
      args: [title, company, description]
    })
    const jobs = await db.execute('SELECT * FROM jobs ORDER BY created_at DESC')
    return jobs.rows.map(job => `
      <div>
        <h2>${job.title}</h2>
        <p>${job.company}</p>
        <p>${job.description}</p>
        <button hx-post="/apply/${job.id}" hx-swap="outerHTML">Apply</button>
      </div>
    `).join('')
  })
 
  // Apply for a job
  .post('/apply/:id', async ({ params, jwt, cookie: { auth } }) => {
    if (!auth) return 'Please login to apply'
    const { sub: userId } = await jwt.verify(auth)
    const jobId = params.id
    await db.execute({
      sql: 'INSERT INTO applications (job_id, user_id) VALUES (?, ?)',
      args: [jobId, userId]
    })
    return 'Application submitted successfully!'
  })
 
  // Google OAuth2 login
  .get('/auth/google', async ({ set }) => {
    const authorizationUrl = oauth.generateAuthorizationURL(
      'https://accounts.google.com',
      googleConfig.clientId,
      googleConfig.redirectUri,
      { scope: 'email profile' }
    )
    set.redirect = authorizationUrl
  })
 
  // Google OAuth2 callback
  .get('/auth/google/callback', async ({ query, set, jwt, setCookie }) => {
    const tokens = await oauth.getTokens(
      'https://accounts.google.com',
      googleConfig.clientId,
      googleConfig.clientSecret,
      googleConfig.redirectUri,
      query.code
    )
    const userInfo = await oauth.getUserInfo('https://www.googleapis.com/oauth2/v3/userinfo', tokens.access_token)
    let user = await db.execute({
      sql: 'SELECT * FROM users WHERE email = ?',
      args: [userInfo.email]
    })
    if (user.rows.length === 0) {
      await db.execute({
        sql: 'INSERT INTO users (email, name, auth_provider) VALUES (?, ?, ?)',
        args: [userInfo.email, userInfo.name, 'google']
      })
      user = await db.execute({
        sql: 'SELECT * FROM users WHERE email = ?',
        args: [userInfo.email]
      })
    }
    const token = await jwt.sign({ sub: user.rows[0].id })
    setCookie('auth', token, { httpOnly: true })
    Set. Redirect = '/'
  })
 
  // GitHub OAuth2 login
  .get('/auth/github', async ({ set }) => {
    Const authorizationUrl = oauth.GenerateAuthorizationURL (
      'https://github.com',
      githubConfig.clientId,
      githubConfig.redirectUri,
      { scope: 'user:email' }
    )
    set.redirect = authorizationUrl
  })
 
  // GitHub OAuth2 callback
  .get('/auth/github/callback', async ({ query, set, jwt, setCookie }) => {
    const tokens = await oauth.getTokens(
      'https://github.com',
      githubConfig.clientId,
      githubConfig.clientSecret,
      githubConfig.redirectUri,
      query.code
    )
    const userInfo = await oauth.getUserInfo('https://api.github.com/user', tokens.access_token)
    let user = await db.execute({
      sql: 'SELECT * FROM users WHERE email = ?',
      args: [userInfo.email]
    })
    if (user.rows.length === 0) {
      await db.execute({
        sql: 'INSERT INTO users (email, name, auth_provider) VALUES (?, ?, ?)',
        args: [userInfo.email, userInfo.name, 'github']
      })
      user = await db.execute({
        sql: 'SELECT * FROM users WHERE email = ?',
        args: [userInfo.email]
      })
    }
    const token = await jwt.sign({ sub: user.rows[0].id })
    setCookie('auth', token, { httpOnly: true })
    set.redirect = '/'
  })
 
  // Logout
  .post('/logout', ({ setCookie, set }) => {
    setCookie('auth', '', { maxAge: 0 })
    set.redirect = '/'
  })
 
  .listen(3000)
 
console.log(`Elysia is running at http://localhost:3000`)

这个扩展版本添加了以下功能:

  1. 用户认证: 支持 Google 和 GitHub 的 OAuth2登录。
  2. 职位搜索: 用户可以通过标题搜索职位。
  3. 应用职位: 已登录的用户可以申请职位。
  4. 用户会话管理: 使用 JWT 来维护用户会话。

要使这个版本工作, 你需要:

  1. 在 Google Cloud Console 和 GitHub Developer settings 中创建 OAuth 应用, 并获取客户端 ID 和密钥。
  2. 更新 Turso 数据库结构。
  3. 替换代码中的占位符 (如数据库 URL、OAuth 客户端 ID 和密钥等)为实际值。
  4. 确保 public 文件夹中有 htmx.min.js 文件。

这个版本提供了一个更完整的招聘网站框架, 包括用户认证、职位搜索和申请功能。你可以根据需要进一步优化 UI/UX, 添加错误处理, 以及实现更多功能, 如用户档案、应用跟踪等。