Skip to content

Commit bb5fad4

Browse files
authored
feat(database): add MySQL database support (#201)
1 parent 0ee5ef3 commit bb5fad4

File tree

6 files changed

+194
-26
lines changed

6 files changed

+194
-26
lines changed

docker-compose.local.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,35 @@ services:
1313
- JWT_SECRET=
1414
- INIT_TABLE=true
1515
- ENABLE_CACHE=true
16+
# MySQL Database Configuration (uncomment to use MySQL instead of SQLite)
17+
# - MYSQL_HOST=mysql
18+
# - MYSQL_PORT=3306
19+
# - MYSQL_USER=newsnow
20+
# - MYSQL_PASSWORD=your_password
21+
# - MYSQL_DATABASE=newsnow
22+
# - MYSQL_SSL=false
23+
# Uncomment the following lines to use MySQL database
24+
# depends_on:
25+
# - mysql
26+
27+
# Uncomment the following service to use MySQL database
28+
# mysql:
29+
# image: mysql:8.0
30+
# container_name: newsnow_mysql
31+
# restart: always
32+
# environment:
33+
# MYSQL_ROOT_PASSWORD: root_password
34+
# MYSQL_DATABASE: newsnow
35+
# MYSQL_USER: newsnow
36+
# MYSQL_PASSWORD: your_password
37+
# ports:
38+
# - "3306:3306"
39+
# volumes:
40+
# - mysql_data:/var/lib/mysql
41+
# command: --default-authentication-plugin=mysql_native_password
1642

1743
volumes:
1844
newsnow_data:
1945
name: newsnow_data
46+
# mysql_data:
47+
# name: newsnow_mysql_data

docker-compose.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,35 @@ services:
1717
- JWT_SECRET=
1818
- INIT_TABLE=true
1919
- ENABLE_CACHE=true
20+
# MySQL Database Configuration (uncomment to use MySQL instead of SQLite)
21+
# - MYSQL_HOST=mysql
22+
# - MYSQL_PORT=3306
23+
# - MYSQL_USER=newsnow
24+
# - MYSQL_PASSWORD=your_password
25+
# - MYSQL_DATABASE=newsnow
26+
# - MYSQL_SSL=false
27+
# Uncomment the following lines to use MySQL database
28+
# depends_on:
29+
# - mysql
30+
31+
# Uncomment the following service to use MySQL database
32+
# mysql:
33+
# image: mysql:8.0
34+
# container_name: newsnow_mysql
35+
# restart: always
36+
# environment:
37+
# MYSQL_ROOT_PASSWORD: root_password
38+
# MYSQL_DATABASE: newsnow
39+
# MYSQL_USER: newsnow
40+
# MYSQL_PASSWORD: your_password
41+
# ports:
42+
# - "3306:3306"
43+
# volumes:
44+
# - mysql_data:/var/lib/mysql
45+
# command: --default-authentication-plugin=mysql_native_password
2046

2147
volumes:
2248
newsnow_data:
2349
name: newsnow_data
50+
# mysql_data:
51+
# name: newsnow_mysql_data

example.env.server

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@ G_CLIENT_ID=
22
G_CLIENT_SECRET=
33
JWT_SECRET=
44
INIT_TABLE=true
5-
ENABLE_CACHE=true
5+
ENABLE_CACHE=true
6+
# MySQL Database Configuration
7+
# Uncomment and configure these variables to use MySQL instead of SQLite
8+
# MYSQL_HOST=localhost
9+
# MYSQL_PORT=3306
10+
# MYSQL_USER=newsnow
11+
# MYSQL_PASSWORD=your_password
12+
# MYSQL_DATABASE=newsnow
13+
# MYSQL_SSL=false

scripts/init-mysql.sql

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
-- MySQL Database Initialization Script for NewsNow
2+
-- Run this script to create the required database and tables
3+
4+
-- Create database if not exists
5+
CREATE DATABASE IF NOT EXISTS newsnow CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
6+
7+
-- Use the database
8+
USE newsnow;
9+
10+
-- Create cache table
11+
CREATE TABLE IF NOT EXISTS cache (
12+
id VARCHAR(255) PRIMARY KEY,
13+
updated BIGINT,
14+
data TEXT
15+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
16+
17+
-- Create user table
18+
CREATE TABLE IF NOT EXISTS user (
19+
id VARCHAR(255) PRIMARY KEY,
20+
email VARCHAR(255),
21+
data TEXT,
22+
type VARCHAR(50),
23+
created BIGINT,
24+
updated BIGINT
25+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
26+
27+
-- Create index for user table
28+
CREATE INDEX IF NOT EXISTS idx_user_id ON user(id);
29+
30+
-- Create MySQL user (optional - you can create this manually)
31+
-- CREATE USER IF NOT EXISTS 'newsnow'@'%' IDENTIFIED BY 'your_password';
32+
-- GRANT ALL PRIVILEGES ON newsnow.* TO 'newsnow'@'%';
33+
-- FLUSH PRIVILEGES;

server/database/cache.ts

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,68 @@ import type { NewsItem } from "@shared/types"
33
import type { Database } from "db0"
44
import type { CacheInfo, CacheRow } from "../types"
55

6+
// 改进的数据库类型检测函数
7+
function isMySQLDatabase(): boolean {
8+
const hasMySQLConfig = process.env.MYSQL_HOST &&
9+
process.env.MYSQL_USER &&
10+
process.env.MYSQL_PASSWORD &&
11+
process.env.MYSQL_DATABASE
12+
13+
if (hasMySQLConfig) {
14+
console.log('🔍 检测到 MySQL 配置,使用 MySQL 数据库')
15+
return true
16+
} else {
17+
console.log('🔍 未检测到 MySQL 配置,使用 SQLite 数据库')
18+
return false
19+
}
20+
}
21+
622
export class Cache {
723
private db
24+
private isMySQL: boolean
25+
826
constructor(db: Database) {
927
this.db = db
28+
this.isMySQL = isMySQLDatabase()
1029
}
1130

1231
async init() {
13-
await this.db.prepare(`
14-
CREATE TABLE IF NOT EXISTS cache (
15-
id TEXT PRIMARY KEY,
16-
updated INTEGER,
17-
data TEXT
18-
);
19-
`).run()
32+
if (this.isMySQL) {
33+
// MySQL syntax
34+
await this.db.prepare(`
35+
CREATE TABLE IF NOT EXISTS cache (
36+
id VARCHAR(255) PRIMARY KEY,
37+
updated BIGINT,
38+
data TEXT
39+
);
40+
`).run()
41+
} else {
42+
// SQLite syntax
43+
await this.db.prepare(`
44+
CREATE TABLE IF NOT EXISTS cache (
45+
id TEXT PRIMARY KEY,
46+
updated INTEGER,
47+
data TEXT
48+
);
49+
`).run()
50+
}
2051
logger.success(`init cache table`)
2152
}
2253

2354
async set(key: string, value: NewsItem[]) {
2455
const now = Date.now()
25-
await this.db.prepare(
26-
`INSERT OR REPLACE INTO cache (id, data, updated) VALUES (?, ?, ?)`,
27-
).run(key, JSON.stringify(value), now)
56+
57+
if (this.isMySQL) {
58+
// MySQL syntax - use ON DUPLICATE KEY UPDATE
59+
await this.db.prepare(
60+
`INSERT INTO cache (id, data, updated) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE data = VALUES(data), updated = VALUES(updated)`,
61+
).run(key, JSON.stringify(value), now)
62+
} else {
63+
// SQLite syntax - use INSERT OR REPLACE
64+
await this.db.prepare(
65+
`INSERT OR REPLACE INTO cache (id, data, updated) VALUES (?, ?, ?)`,
66+
).run(key, JSON.stringify(value), now)
67+
}
2868
logger.success(`set ${key} cache`)
2969
}
3070

server/database/user.ts

Lines changed: 46 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import process from "node:process"
12
import type { Database } from "db0"
23
import type { UserInfo } from "#/types"
34

@@ -8,28 +9,58 @@ export class UserTable {
89
}
910

1011
async init() {
11-
await this.db.prepare(`
12-
CREATE TABLE IF NOT EXISTS user (
13-
id TEXT PRIMARY KEY,
14-
email TEXT,
15-
data TEXT,
16-
type TEXT,
17-
created INTEGER,
18-
updated INTEGER
19-
);
20-
`).run()
21-
await this.db.prepare(`
22-
CREATE INDEX IF NOT EXISTS idx_user_id ON user(id);
23-
`).run()
12+
// Check if we're using MySQL by looking for MySQL environment variables
13+
const isMySQL = process.env.MYSQL_HOST && process.env.MYSQL_USER && process.env.MYSQL_PASSWORD && process.env.MYSQL_DATABASE
14+
15+
if (isMySQL) {
16+
// MySQL syntax
17+
await this.db.prepare(`
18+
CREATE TABLE IF NOT EXISTS user (
19+
id VARCHAR(255) PRIMARY KEY,
20+
email VARCHAR(255),
21+
data TEXT,
22+
type VARCHAR(50),
23+
created BIGINT,
24+
updated BIGINT
25+
);
26+
`).run()
27+
await this.db.prepare(`
28+
CREATE INDEX IF NOT EXISTS idx_user_id ON user(id);
29+
`).run()
30+
} else {
31+
// SQLite syntax
32+
await this.db.prepare(`
33+
CREATE TABLE IF NOT EXISTS user (
34+
id TEXT PRIMARY KEY,
35+
email TEXT,
36+
data TEXT,
37+
type TEXT,
38+
created INTEGER,
39+
updated INTEGER
40+
);
41+
`).run()
42+
await this.db.prepare(`
43+
CREATE INDEX IF NOT EXISTS idx_user_id ON user(id);
44+
`).run()
45+
}
2446
logger.success(`init user table`)
2547
}
2648

2749
async addUser(id: string, email: string, type: "github") {
2850
const u = await this.getUser(id)
2951
const now = Date.now()
52+
const isMySQL = process.env.MYSQL_HOST && process.env.MYSQL_USER && process.env.MYSQL_PASSWORD && process.env.MYSQL_DATABASE
53+
3054
if (!u) {
31-
await this.db.prepare(`INSERT INTO user (id, email, data, type, created, updated) VALUES (?, ?, ?, ?, ?, ?)`)
32-
.run(id, email, "", type, now, now)
55+
if (isMySQL) {
56+
// MySQL syntax - use ON DUPLICATE KEY UPDATE
57+
await this.db.prepare(`INSERT INTO user (id, email, data, type, created, updated) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE email = VALUES(email), updated = VALUES(updated)`)
58+
.run(id, email, "", type, now, now)
59+
} else {
60+
// SQLite syntax
61+
await this.db.prepare(`INSERT INTO user (id, email, data, type, created, updated) VALUES (?, ?, ?, ?, ?, ?)`)
62+
.run(id, email, "", type, now, now)
63+
}
3364
logger.success(`add user ${id}`)
3465
} else if (u.email !== email && u.type !== type) {
3566
await this.db.prepare(`UPDATE user SET email = ?, updated = ? WHERE id = ?`).run(email, now, id)

0 commit comments

Comments
 (0)