darkfire514 commited on
Commit
b8f3220
·
verified ·
1 Parent(s): 6a977ba

Update README.md

Browse files
Files changed (1) hide show
  1. README.md +487 -501
README.md CHANGED
@@ -1,715 +1,701 @@
1
- ---
2
- title: PostgreSQL General
3
- emoji: 🏃
4
- colorFrom: blue
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- ---
9
 
10
- # PostgreSQL Adminer on Hugging Face Space
11
 
12
- 这是一个用于在 Hugging Face Docker Space 上部署 PostgreSQL 数据库 Adminer 管理界面的项目
13
 
14
- 本项目采用以下设计
15
 
16
- ```text
17
- PostgreSQL 程序:运行在 Hugging Face Space 容器内
18
- PostgreSQL 数据目录:使用 Space 本地磁盘 /home/user/pgdata
19
- 挂载大磁盘 /data:用于保存备份、用户文件、导出文件和生成文件
20
- Adminer Web 管理界面:通过 7860 端口访问
21
  ```
22
 
23
- 不建议把 PostgreSQL 的运行数据目录直接放到挂载磁盘 `/data/postgres`,因为 PostgreSQL 对底层文件系统权限、锁、随机读写、WAL、fsync 等要求较高。挂载磁盘更适合保存备份文件和业务文件。
24
 
25
- ---
26
-
27
- ## 1. 项目结构
 
 
28
 
29
- 根目录包含以下文件
30
 
31
  ```text
32
- README.md
33
- Dockerfile
34
- start.sh
 
 
 
35
  ```
36
 
37
- ---
38
 
39
- ## 2. 整体架构
40
 
41
- ```text
42
- 浏览器
43
-
44
- Hugging Face Space Web 入口,端口 7860
45
-
46
- Adminer 管理界面
47
-
48
- PostgreSQL,容器内部 5432 端口
49
- ```
50
 
51
- 目录设计
52
 
53
  ```text
54
- /home/user/pgdata
55
- └── PostgreSQL 正在运行的数据库文件
56
-
57
- /data
58
- ├── backups
59
- │ ├── latest.sql
60
- │ └── backup_appdb_YYYYMMDD_HHMMSS.sql
61
- ├── files
62
- │ └── 用户上传文件
63
- ├── exports
64
- │ └── 导出的 CSV / JSON / SQL 文件
65
- └── generated
66
- └── AI 生成文件、图片、视频等
67
  ```
68
 
69
- 数据库中建议只保存文件路径、文件名、文件大小、MIME 类型等索引信息不建议把大文件直接写入 PostgreSQL。
70
 
71
- ---
 
 
 
 
72
 
73
- ## 3. Dockerfile
74
 
75
- ```dockerfile
76
- FROM postgres:17
77
 
78
- ENV POSTGRES_USER=admin
79
- ENV POSTGRES_DB=appdb
80
 
81
- # PostgreSQL 运行中的数据库目录:使用 Space 本地磁盘
82
- ENV PGDATA=/home/user/pgdata
83
 
84
- # 挂载大磁盘目录:用于备份、用户文件、导出文件
85
- ENV DATA_DIR=/data
86
- ENV BACKUP_DIR=/data/backups
87
- ENV USER_FILE_DIR=/data/files
88
- ENV EXPORT_DIR=/data/exports
89
- ENV GENERATED_DIR=/data/generated
90
 
91
- # 自动备份间隔,默认 1 小时
92
- ENV BACKUP_INTERVAL_SECONDS=3600
93
 
94
- # Adminer 版本
95
- ENV ADMINER_VERSION=4.8.1
 
 
96
 
97
- RUN apt-get update && apt-get install -y --no-install-recommends \
98
- curl \
99
- php \
100
- php-pgsql \
101
- ca-certificates \
102
- procps \
103
- && rm -rf /var/lib/apt/lists/*
104
 
105
- RUN curl -L -o /adminer.php \
106
- https://github.com/vrana/adminer/releases/download/v${ADMINER_VERSION}/adminer-${ADMINER_VERSION}.php
107
 
108
- COPY start.sh /start.sh
109
- RUN chmod +x /start.sh
110
 
111
- EXPOSE 5432
112
- EXPOSE 7860
113
 
114
- CMD ["/start.sh"]
 
115
  ```
116
 
117
- ---
118
-
119
- ## 4. start.sh
120
 
121
- ```bash
122
- #!/usr/bin/env bash
123
- set -e
124
-
125
- echo "======================================"
126
- echo "PostgreSQL + Adminer for Hugging Face"
127
- echo "======================================"
128
-
129
- if [ -z "$POSTGRES_PASSWORD" ]; then
130
- echo "ERROR: POSTGRES_PASSWORD is not set."
131
- echo "Please set POSTGRES_PASSWORD in Hugging Face Space Secrets."
132
- exit 1
133
- fi
134
-
135
- export POSTGRES_USER="${POSTGRES_USER:-admin}"
136
- export POSTGRES_DB="${POSTGRES_DB:-appdb}"
137
- export PGDATA="${PGDATA:-/home/user/pgdata}"
138
-
139
- export DATA_DIR="${DATA_DIR:-/data}"
140
- export BACKUP_DIR="${BACKUP_DIR:-/data/backups}"
141
- export USER_FILE_DIR="${USER_FILE_DIR:-/data/files}"
142
- export EXPORT_DIR="${EXPORT_DIR:-/data/exports}"
143
- export GENERATED_DIR="${GENERATED_DIR:-/data/generated}"
144
- export BACKUP_INTERVAL_SECONDS="${BACKUP_INTERVAL_SECONDS:-3600}"
145
-
146
- echo "POSTGRES_USER=$POSTGRES_USER"
147
- echo "POSTGRES_DB=$POSTGRES_DB"
148
- echo "PGDATA=$PGDATA"
149
- echo "DATA_DIR=$DATA_DIR"
150
- echo "BACKUP_DIR=$BACKUP_DIR"
151
- echo "USER_FILE_DIR=$USER_FILE_DIR"
152
- echo "EXPORT_DIR=$EXPORT_DIR"
153
- echo "GENERATED_DIR=$GENERATED_DIR"
154
- echo "BACKUP_INTERVAL_SECONDS=$BACKUP_INTERVAL_SECONDS"
155
-
156
- echo "Creating directories..."
157
-
158
- mkdir -p "$PGDATA"
159
- mkdir -p "$BACKUP_DIR"
160
- mkdir -p "$USER_FILE_DIR"
161
- mkdir -p "$EXPORT_DIR"
162
- mkdir -p "$GENERATED_DIR"
163
-
164
- # PostgreSQL 数据目录需要 postgres 用户权限
165
- chown -R postgres:postgres "$PGDATA" || true
166
- chmod 700 "$PGDATA" || true
167
-
168
- # /data 是挂载大磁盘,可能不支持 chown,所以这里允许失败
169
- chmod -R 755 "$DATA_DIR" || true
170
-
171
- echo "Directory status:"
172
- ls -ld "$PGDATA" || true
173
- ls -ld "$DATA_DIR" || true
174
- ls -ld "$BACKUP_DIR" || true
175
- ls -ld "$USER_FILE_DIR" || true
176
- ls -ld "$EXPORT_DIR" || true
177
- ls -ld "$GENERATED_DIR" || true
178
-
179
- RESTORE_BACKUP=0
180
-
181
- if [ ! -s "$PGDATA/PG_VERSION" ]; then
182
- echo "No existing PostgreSQL data directory found."
183
-
184
- if [ -f "$BACKUP_DIR/latest.sql" ]; then
185
- echo "Backup file found: $BACKUP_DIR/latest.sql"
186
- echo "Database will be restored after PostgreSQL initialization."
187
- RESTORE_BACKUP=1
188
- else
189
- echo "No backup file found. A new database will be initialized."
190
- fi
191
- else
192
- echo "Existing PostgreSQL data directory found."
193
- fi
194
-
195
- echo "Starting PostgreSQL..."
196
- docker-entrypoint.sh postgres &
197
-
198
- POSTGRES_PID=$!
199
-
200
- echo "Waiting for PostgreSQL to become ready..."
201
-
202
- until pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; do
203
- sleep 2
204
- done
205
-
206
- echo "PostgreSQL is ready."
207
-
208
- if [ "$RESTORE_BACKUP" = "1" ]; then
209
- echo "Restoring database from $BACKUP_DIR/latest.sql ..."
210
-
211
- psql \
212
- -h 127.0.0.1 \
213
- -U "$POSTGRES_USER" \
214
- -d "$POSTGRES_DB" \
215
- < "$BACKUP_DIR/latest.sql" || {
216
- echo "WARNING: Restore failed. Please check backup file."
217
- }
218
 
219
- echo "Restore process finished."
220
- fi
221
 
222
- create_backup() {
223
- TIMESTAMP="$(date +%Y%m%d_%H%M%S)"
224
- BACKUP_FILE="$BACKUP_DIR/backup_${POSTGRES_DB}_${TIMESTAMP}.sql"
225
- LATEST_FILE="$BACKUP_DIR/latest.sql"
226
 
227
- echo "Creating backup: $BACKUP_FILE"
228
 
229
- pg_dump \
230
- -h 127.0.0.1 \
231
- -U "$POSTGRES_USER" \
232
- -d "$POSTGRES_DB" \
233
- > "$BACKUP_FILE.tmp"
234
 
235
- mv "$BACKUP_FILE.tmp" "$BACKUP_FILE"
236
- cp "$BACKUP_FILE" "$LATEST_FILE"
237
 
238
- echo "Backup saved:"
239
- echo "- $BACKUP_FILE"
240
- echo "- $LATEST_FILE"
241
- }
242
 
243
- backup_loop() {
244
- echo "Backup loop started. Interval: ${BACKUP_INTERVAL_SECONDS}s"
245
 
246
- while true; do
247
- sleep "$BACKUP_INTERVAL_SECONDS"
248
 
249
- if pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; then
250
- create_backup || echo "WARNING: Backup failed."
251
- else
252
- echo "WARNING: PostgreSQL is not ready. Backup skipped."
253
- fi
254
- done
255
- }
256
 
257
- # 启动后先做一次备份,避免 latest.sql 长时间不存在
258
- if pg_isready -h 127.0.0.1 -p 5432 -U "$POSTGRES_USER"; then
259
- create_backup || echo "WARNING: Initial backup failed."
260
- fi
261
 
262
- backup_loop &
 
 
263
 
264
- echo "Starting Adminer on port 7860..."
265
- echo "Adminer login:"
266
- echo "System: PostgreSQL"
267
- echo "Server: 127.0.0.1"
268
- echo "Username: $POSTGRES_USER"
269
- echo "Database: $POSTGRES_DB"
270
 
271
- php -S 0.0.0.0:7860 /adminer.php
272
 
273
- wait "$POSTGRES_PID"
 
 
 
 
 
274
  ```
275
 
276
  ---
277
 
278
- ## 5. Hugging Face Space 配置
279
 
280
- 创建 Hugging Face Space 时选择
281
 
282
- ```text
283
- SDK: Docker
284
- Visibility: Public 或 Private
285
- Hardware: CPU Basic 即可
286
  ```
287
 
288
- README 顶部需要包含:
289
 
290
- ```yaml
291
- ---
292
- title: PostgreSQL Adminer
293
- emoji: 🐘
294
- colorFrom: blue
295
- colorTo: indigo
296
- sdk: docker
297
- app_port: 7860
298
- pinned: false
299
  ---
300
- ```
301
 
302
- 其中:
303
 
304
- | 配置 | 说明 |
305
- | ---------------- | -------------------------------- |
306
- | `sdk: docker` | 使用 Dockerfile 构建 Space |
307
- | `app_port: 7860` | Hugging Face 对外暴露 Adminer Web 页面 |
308
- | `title` | Space 显示名称 |
309
- | `emoji` | Space 图标 |
310
 
311
- ---
312
-
313
- ## 6. 环境变量
314
-
315
- 在 Hugging Face Space 的:
316
 
317
- ```text
318
- Settings Variables and secrets
319
  ```
320
 
321
- 中配置环境变量。
322
-
323
- ### 必填 Secret
324
 
325
- | 变量名 | 类型 | 示例 | 说明 |
326
- | ------------------- | ------ | ---------------------- | ---------------- |
327
- | `POSTGRES_PASSWORD` | Secret | `your_strong_password` | PostgreSQL 管理员密码 |
328
 
329
- ### 推荐 Variables
330
 
331
- | 变量名 | 默认值 | 说明 |
332
- | ------------------------- | ------------------- | ----------------- |
333
- | `POSTGRES_USER` | `admin` | PostgreSQL 管理员用户名 |
334
- | `POSTGRES_DB` | `appdb` | 默认数据库名称 |
335
- | `PGDATA` | `/home/user/pgdata` | PostgreSQL 运行数据目录 |
336
- | `DATA_DIR` | `/data` | 挂载大磁盘根目录 |
337
- | `BACKUP_DIR` | `/data/backups` | 数据库备份目录 |
338
- | `USER_FILE_DIR` | `/data/files` | 用户上传文件目录 |
339
- | `EXPORT_DIR` | `/data/exports` | 导出文件目录 |
340
- | `GENERATED_DIR` | `/data/generated` | AI 生成文件目录 |
341
- | `BACKUP_INTERVAL_SECONDS` | `3600` | 自动备份间隔,单位为秒 |
342
 
343
- 示例
344
 
345
- ```env
346
- POSTGRES_USER=admin
347
- POSTGRES_DB=appdb
348
- PGDATA=/home/user/pgdata
349
- DATA_DIR=/data
350
- BACKUP_DIR=/data/backups
351
- USER_FILE_DIR=/data/files
352
- EXPORT_DIR=/data/exports
353
- GENERATED_DIR=/data/generated
354
- BACKUP_INTERVAL_SECONDS=3600
355
  ```
356
 
357
- 不要把 `POSTGRES_PASSWORD` 写进代码仓库或 README 的真实值中应放入 Hugging Face Secret
358
 
359
  ---
360
 
361
- ## 7. 挂载磁盘设置
362
 
363
- 建议将 Hugging Face Storage Bucket 挂载到
364
 
365
  ```text
366
- /data
367
  ```
368
 
369
- 推荐配置:
 
 
370
 
371
  ```text
372
- Mount path: /data
373
- Access mode: Read & Write
374
- Bucket visibility: Private
375
  ```
376
 
377
- 挂载磁盘 `/data` 用于保存
378
 
379
  ```text
380
- /data/backups 数据库备份
381
- /data/files 用户上传文件
382
- /data/exports 导出文件
383
- /data/generated AI 生成文件
 
384
  ```
385
 
386
- 不建议将 PostgreSQL 的 `PGDATA` 设置为 `/data/postgres`
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
  ---
389
 
390
- ## 8. Adminer 登录方式
391
 
392
- 署完成后打开 Hugging Face Space 页面,会看到 Adminer 登录界面
393
 
394
- 填写
395
 
396
  ```text
397
- System: PostgreSQL
398
- Server: 127.0.0.1
399
- Username: admin
400
- Password: 你的 POSTGRES_PASSWORD
401
- Database: appdb
402
  ```
403
 
404
- 如果你修改了环境变量,请使自己的值:
 
 
405
 
406
  ```text
407
- Username: POSTGRES_USER
408
- Database: POSTGRES_DB
 
 
 
 
409
  ```
410
 
411
  ---
412
 
413
- ## 9. 数据保存逻辑
414
 
415
- 本项目采“数据库索引 + 文件系统存储”的设计
416
 
417
- 不推荐把大文件直接写入 PostgreSQL,例如
418
 
419
- ```sql
420
- CREATE TABLE files (
421
- id SERIAL PRIMARY KEY,
422
- content BYTEA
423
- );
424
  ```
425
 
426
- 推荐把文件保存到 `/data/files`数据库只保存文件路径
427
 
428
- ```sql
429
- CREATE TABLE files (
430
- id SERIAL PRIMARY KEY,
431
- user_id TEXT,
432
- filename TEXT NOT NULL,
433
- file_path TEXT NOT NULL,
434
- file_size BIGINT,
435
- mime_type TEXT,
436
- created_at TIMESTAMP DEFAULT now()
437
- );
438
  ```
439
 
440
- 示例记录:
441
-
442
- ```text
443
- filename: image_001.png
444
- file_path: /data/files/image_001.png
445
- file_size: 245891
446
- mime_type: image/png
447
- ```
448
 
449
  ---
450
 
451
- ## 10. 自动备份机制
452
 
453
- 启动脚本会自动执行备份
454
 
455
- 默认备份间隔
456
 
457
- ```text
458
- 3600 秒,即 1 小时
459
- ```
 
 
460
 
461
- 可以通过环境变量修改:
462
 
463
- ```env
464
- BACKUP_INTERVAL_SECONDS=1800
465
- ```
 
 
466
 
467
- 备份文件保存到:
468
 
469
- ```text
470
- /data/backups
471
- ```
472
 
473
- 文件示例:
 
474
 
475
- ```text
476
- /data/backups/latest.sql
477
- /data/backups/backup_appdb_20260529_180000.sql
478
- ```
479
-
480
- 其中:
481
 
482
- ```text
483
- latest.sql
484
- ```
485
 
486
- 始终指向最近一次备份内容。
487
 
488
- ---
489
-
490
- ## 11. 自动恢复机制
 
 
 
491
 
492
- 如果 Space 重启或重建后,`/home/user/pgdata` 不存在,但 `/data/backups/latest.sql` 存在,启动脚本会尝试自动恢复:
493
 
494
- ```text
495
- /data/backups/latest.sql
496
-
497
- PostgreSQL appdb
498
- ```
499
 
500
- 注意:
 
 
 
 
 
501
 
502
- ```text
503
- 1. 自动恢复依赖 latest.sql 是否存在
504
- 2. 如果 latest.sql 损坏,恢复会失败
505
- 3. 建议定期检查 /data/backups 下的备份文件
506
- 4. 重要数据库建议额外同步到 R2 / S3 / Hugging Face Dataset
507
  ```
508
 
509
  ---
510
 
511
- ## 12. 本地测试
512
 
513
- 镜像:
514
 
515
- ```bash
516
- docker build -t hf-postgres-adminer .
 
 
 
 
 
 
 
 
 
 
517
  ```
518
 
519
- 运行容器
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520
 
521
- ```bash
522
- docker run --rm -it \
523
- -p 7860:7860 \
524
- -p 5432:5432 \
525
- -e POSTGRES_PASSWORD=your_strong_password \
526
- -v $(pwd)/data:/data \
527
- hf-postgres-adminer
528
- ```
529
 
530
- 访问 Adminer:
 
 
531
 
532
- ```text
533
- http://localhost:7860
534
- ```
535
 
536
- 本地连接 PostgreSQL:
 
 
 
 
 
 
 
537
 
538
- ```bash
539
- psql "postgresql://admin:your_strong_password@127.0.0.1:5432/appdb"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  ```
541
 
542
  ---
543
 
544
- ## 13. 常用 PostgreSQL 命令
545
-
546
- 进入数据库:
547
 
548
- ```bash
549
- psql -h 127.0.0.1 -U admin -d appdb
550
- ```
551
 
552
- 查看数据库
553
 
554
  ```sql
555
- \l
 
 
 
 
 
 
 
 
556
  ```
557
 
558
- 查看当前数据库中的表
559
-
560
- ```sql
561
- \dt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  ```
563
 
564
- 创建数据库:
565
 
566
- ```sql
567
- CREATE DATABASE project_db;
568
- ```
569
 
570
- 创建用户
571
 
572
- ```sql
573
- CREATE USER project_user WITH PASSWORD 'replace_with_strong_password';
574
  ```
575
 
576
- 授权
577
 
578
- ```sql
579
- GRANT ALL PRIVILEGES ON DATABASE project_db TO project_user;
 
 
580
  ```
581
 
582
- ---
583
-
584
- ## 14. 多项目隔离建议
585
 
586
- 如果这个 PostgreSQL Space 要服务多个项目,不建议所有项目共用 `admin` 用户。
 
 
 
 
 
 
587
 
588
- 推荐
589
 
590
  ```text
591
- 一个项目一个 database
592
- 一个项目一个 user
593
- 一个项目一个 password
 
 
 
594
  ```
595
 
596
- 示例:
597
 
598
- ```sql
599
- CREATE DATABASE rss_project;
600
- CREATE USER rss_user WITH PASSWORD 'rss_project_password';
601
- GRANT ALL PRIVILEGES ON DATABASE rss_project TO rss_user;
602
 
603
- CREATE DATABASE image_project;
604
- CREATE USER image_user WITH PASSWORD 'image_project_password';
605
- GRANT ALL PRIVILEGES ON DATABASE image_project TO image_user;
606
- ```
607
 
608
- 业务项目连接时使用自己的数据库用户
609
 
610
- ```env
611
- DATABASE_URL=postgresql://rss_user:rss_project_password@host:5432/rss_project
612
- ```
 
 
613
 
614
- ---
 
 
 
 
 
615
 
616
- ## 15. 安全建议
617
 
618
- 如果 Space 是 Public,请特别注意安全。
 
 
 
 
 
619
 
620
- 建议
621
 
622
- ```text
623
- 1. 不要把 POSTGRES_PASSWORD 写进代码仓库
624
- 2. 使用 Hugging Face Secrets 保存数据库密码
625
- 3. Adminer 必须使用强密码
626
- 4. 不建议直接暴露 PostgreSQL 5432 给公网
627
- 5. 尽量在外层增加 Cloudflare Access / OAuth / API Gateway
628
- 6. 不要让业务项目使用 admin 超级用户
629
- 7. 每个项目创建独立 database 和 user
630
- 8. 定期备份数据库
631
- 9. 重要备份同步到外部对象存储
632
- 10. 不要在公开日志中输出数据库密码
633
  ```
634
 
635
  ---
636
 
637
- ## 16. 适用场景
 
 
638
 
639
- 适合
640
 
641
  ```text
642
- 个人项目数据库
643
- AI Demo 数据库
644
- RSS / 爬虫 / 内容索引数据库
645
- 测试环境数据库
646
- 公开项目的轻量级共享数据库
647
- Hugging Face Space 内部应用数据库
648
- AI 生成文件索引系统
649
- 用户文件索引系统
650
  ```
651
 
652
- 适合:
 
 
653
 
654
  ```text
655
- 高并发生产数据库
656
- 金融交易核心数据库
657
- 强一致性要求极高的数据库
658
- 需要专业数据库 SLA 的业务
659
- 需要大量公网 PostgreSQL 长连接的业务
660
  ```
661
 
662
  ---
663
 
664
- ## 17. 默认连接信息
665
 
666
- Adminer Web
667
 
668
- ```text
669
- https://你的-space.hf.space
 
670
  ```
671
 
672
- PostgreSQL
673
 
674
- ```text
675
- Host: 127.0.0.1
676
- Port: 5432
677
- User: admin
678
- Database: appdb
679
- Data Directory: /home/user/pgdata
680
- Backup Directory: /data/backups
681
- User File Directory: /data/files
 
 
 
682
  ```
683
 
684
- 注意`127.0.0.1:5432` 是容器内部地址,主要给 Adminer 或同容器内应用使用外部项目不建议直接连接该端口,推荐通过 API Gateway 或受控后端访问数据库。
 
 
 
 
 
 
 
 
 
 
 
 
 
685
 
686
  ---
687
 
688
- ## 18. 重要说明
689
 
690
- 本项目基于官方 PostgreSQL Docker 镜像
691
 
692
  ```text
693
- postgres:17
 
 
 
 
 
 
694
  ```
695
 
696
- 部署时不需要拉取 `docker-library/postgres` 源码,只需要在 Dockerfile 中使用
697
 
698
- ```dockerfile
699
- FROM postgres:17
 
 
 
 
700
  ```
701
 
702
- 即可。
 
 
703
 
704
- 本项目的关键设计是
705
 
706
  ```text
707
- PostgreSQL 运行数据目录:/home/user/pgdata
708
- 挂载大磁盘:/data
709
- 数据库备份:/data/backups
710
- 用户文件:/data/files
711
- 导出文件:/data/exports
712
- 生成文件:/data/generated
713
- ```
714
-
715
- 这样可以避免 PostgreSQL 直接在挂载 Bucket 上运行时出现文件权限或文件系统兼容问题,同时又能利用大容量挂载磁盘保存备份和业务文件。
 
 
 
 
 
 
 
1
+ ## 14. 用户账号机制说明
 
 
 
 
 
 
 
2
 
3
+ 本项目不提供公开注册功能。
4
 
5
+ PostgreSQL 用户账号不是网站账号,也不是任何人都可以自助注册的账号。所有数据库用户、数据库、schema 和权限都必须由管理员通过 Adminer 或 SQL 命令手动创建
6
 
7
+ 默认初始化管理员账号由以下环境变量决定
8
 
9
+ ```env
10
+ POSTGRES_USER=admin
11
+ POSTGRES_PASSWORD=your_strong_password
12
+ POSTGRES_DB=appdb
 
13
  ```
14
 
15
+ 默认管理员:
16
 
17
+ ```text
18
+ Username: admin
19
+ Database: appdb
20
+ Password: POSTGRES_PASSWORD
21
+ ```
22
 
23
+ `admin` 是数据管理员账号,拥有较高权限,主要用于
24
 
25
  ```text
26
+ 1. 登录 Adminer
27
+ 2. 创建数据库
28
+ 3. 创建项目用户
29
+ 4. 分配数据库权限
30
+ 5. 查看和维护数据库
31
+ 6. 执行备份、恢复、迁移等管理操作
32
  ```
33
 
34
+ 不建议把 `admin` 账号直接提供给外部项目或普通用户使用。
35
 
36
+ ---
37
 
38
+ ## 15. 管理员如何创建项目数据库和账号
 
 
 
 
 
 
 
 
39
 
40
+ 推荐原则
41
 
42
  ```text
43
+ 一个项目一个 database
44
+ 一个项目一个 user
45
+ 一个项目一个 password
 
 
 
 
 
 
 
 
 
 
46
  ```
47
 
48
+ 例如,管理员要为 RSS 项目创建独立数据库和账号可以登录 Adminer 后执行以下 SQL:
49
 
50
+ ```sql
51
+ CREATE DATABASE rss_project;
52
+ CREATE USER rss_user WITH PASSWORD 'replace_with_strong_password';
53
+ GRANT ALL PRIVILEGES ON DATABASE rss_project TO rss_user;
54
+ ```
55
 
56
+ 然后切换到 `rss_project` 数据库,继续执行 schema 权限配置:
57
 
58
+ ```sql
59
+ GRANT ALL ON SCHEMA public TO rss_user;
60
 
61
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
62
+ GRANT ALL ON TABLES TO rss_user;
63
 
64
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
65
+ GRANT ALL ON SEQUENCES TO rss_user;
66
 
67
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
68
+ GRANT ALL ON FUNCTIONS TO rss_user;
69
+ ```
 
 
 
70
 
71
+ 完整示例:
 
72
 
73
+ ```sql
74
+ CREATE DATABASE image_project;
75
+ CREATE USER image_user WITH PASSWORD 'image_project_strong_password';
76
+ GRANT ALL PRIVILEGES ON DATABASE image_project TO image_user;
77
 
78
+ \c image_project
 
 
 
 
 
 
79
 
80
+ GRANT ALL ON SCHEMA public TO image_user;
 
81
 
82
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
83
+ GRANT ALL ON TABLES TO image_user;
84
 
85
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
86
+ GRANT ALL ON SEQUENCES TO image_user;
87
 
88
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
89
+ GRANT ALL ON FUNCTIONS TO image_user;
90
  ```
91
 
92
+ 创建完成后,业务项目可以使用以下连接格式:
 
 
93
 
94
+ ```env
95
+ DATABASE_URL=postgresql://image_user:image_project_strong_password@127.0.0.1:5432/image_project
96
+ ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ 注意:`127.0.0.1:5432` 是容器内部地址,适用于同一个 Space 容器内的应用。如果外部项目需要访问数据库,不建议直接暴露 PostgreSQL 端口,推荐使用 API Gateway。
 
99
 
100
+ ---
 
 
 
101
 
102
+ ## 16. 创建只读账号
103
 
104
+ 如果需要给某个用户或项目只读权限,可以创建只读用户。
 
 
 
 
105
 
106
+ 示例:
 
107
 
108
+ ```sql
109
+ CREATE USER readonly_user WITH PASSWORD 'readonly_strong_password';
 
 
110
 
111
+ GRANT CONNECT ON DATABASE rss_project TO readonly_user;
112
+ ```
113
 
114
+ 切换到目标数据库:
 
115
 
116
+ ```sql
117
+ \c rss_project
118
+ ```
 
 
 
 
119
 
120
+ 授予只读权限:
 
 
 
121
 
122
+ ```sql
123
+ GRANT USAGE ON SCHEMA public TO readonly_user;
124
+ GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;
125
 
126
+ ALTER DEFAULT PRIVILEGES IN SCHEMA public
127
+ GRANT SELECT ON TABLES TO readonly_user;
128
+ ```
 
 
 
129
 
130
+ 只读账号适合:
131
 
132
+ ```text
133
+ 1. 数据分析
134
+ 2. BI 看板
135
+ 3. 外部查询服务
136
+ 4. 临时审查
137
+ 5. 只读 API 服务
138
  ```
139
 
140
  ---
141
 
142
+ ## 17. 修改用户密码
143
 
144
+ 管理员可以使用以下 SQL 修改用户密码
145
 
146
+ ```sql
147
+ ALTER USER rss_user WITH PASSWORD 'new_strong_password';
 
 
148
  ```
149
 
150
+ 修改后,使用旧密码的应用需要同步更新环境变量。
151
 
 
 
 
 
 
 
 
 
 
152
  ---
 
153
 
154
+ ## 18. 删除用户和数据库
155
 
156
+ 删除用户前,需要先确认该用户不再拥有数据库对象。
 
 
 
 
 
157
 
158
+ 删除用户:
 
 
 
 
159
 
160
+ ```sql
161
+ DROP USER rss_user;
162
  ```
163
 
164
+ 删除数据库:
 
 
165
 
166
+ ```sql
167
+ DROP DATABASE rss_project;
168
+ ```
169
 
170
+ 如果用户拥有表、sequence、函数等对象,可以先在对应数据库内执行:
171
 
172
+ ```sql
173
+ REASSIGN OWNED BY rss_user TO admin;
174
+ DROP OWNED BY rss_user;
175
+ ```
 
 
 
 
 
 
 
176
 
177
+ 然后再删除用户
178
 
179
+ ```sql
180
+ DROP USER rss_user;
 
 
 
 
 
 
 
 
181
  ```
182
 
183
+ 危险操作请谨慎执行建议先备份数据库
184
 
185
  ---
186
 
187
+ ## 19. 不建议直接对外暴露 PostgreSQL 端口
188
 
189
+ 本项目默认暴露给 Hugging Face Space Web 端口是
190
 
191
  ```text
192
+ 7860
193
  ```
194
 
195
+ 该端口用于访问 Adminer 或 API 服务。
196
+
197
+ PostgreSQL 默认端口是:
198
 
199
  ```text
200
+ 5432
 
 
201
  ```
202
 
203
+ 不建议将 `5432` 直接暴露给公网。原因包括
204
 
205
  ```text
206
+ 1. PostgreSQL 是数据库服务,不是公开 Web 服务
207
+ 2. 公网暴露 5432 容易被扫描和攻击
208
+ 3. 数据库长连接在 Hugging Face Space 场景下不稳定
209
+ 4. 权限泄露风险较高
210
+ 5. 不便于统一鉴权、限流、日志和审计
211
  ```
212
 
213
+ 推荐对外提供 API,而不是让外部项目直接连接数据库
214
+
215
+ 推荐架构:
216
+
217
+ ```text
218
+ 外部项目 / 用户
219
+
220
+ API Token 鉴权
221
+
222
+ FastAPI / API Gateway
223
+
224
+ PostgreSQL
225
+ ```
226
 
227
  ---
228
 
229
+ ## 20. 对外提供 API 调用接口的推荐方式
230
 
231
+ 如果需要让外项目调用数据库建议在当前 Space 中增加一个 FastAPI 服务
232
 
233
+ 推荐公开 API
234
 
235
  ```text
236
+ POST /api/query 执行受控查询
237
+ POST /api/insert 写入数据
238
+ GET /api/health 健康检查
239
+ GET /api/files 查询文件索引
240
+ POST /api/files 创建文件索引
241
  ```
242
 
243
+ 不建议开放任意 SQL 执行接口给普通户。
244
+
245
+ 推荐做法是:
246
 
247
  ```text
248
+ 1. 管理员在 PostgreSQL 中创建项目数据库和项目用户
249
+ 2. 管理员在 Space Secrets 中配置项目数据库连接串
250
+ 3. FastAPI 使用项目账号连接数据库
251
+ 4. 外部项目通过 API Token 调用 FastAPI
252
+ 5. FastAPI 执行受控 SQL
253
+ 6. PostgreSQL 不直接暴露给公网
254
  ```
255
 
256
  ---
257
 
258
+ ## 21. API 鉴权方式
259
 
260
+ 推荐使API Token
261
 
262
+ Hugging Face Space 的 Secrets 中配置
263
 
264
+ ```env
265
+ API_TOKEN=replace_with_random_api_token
 
 
 
266
  ```
267
 
268
+ 外部项目请求 API 时在 Header 携带
269
 
270
+ ```http
271
+ Authorization: Bearer replace_with_random_api_token
 
 
 
 
 
 
 
 
272
  ```
273
 
274
+ 服务端验证通过后才允许访问数据库。
 
 
 
 
 
 
 
275
 
276
  ---
277
 
278
+ ## 22. FastAPI 示例:健康检查接口
279
 
280
+ 如果需要增加 API 服务,可以在项目中新增 `api.py`
281
 
282
+ 示例
283
 
284
+ ```python
285
+ import os
286
+ import psycopg2
287
+ from fastapi import FastAPI, Header, HTTPException
288
+ from pydantic import BaseModel
289
 
290
+ app = FastAPI()
291
 
292
+ API_TOKEN = os.environ.get("API_TOKEN")
293
+ DATABASE_URL = os.environ.get(
294
+ "DATABASE_URL",
295
+ "postgresql://admin:password@127.0.0.1:5432/appdb"
296
+ )
297
 
 
298
 
299
+ def verify_token(authorization: str | None):
300
+ if not API_TOKEN:
301
+ raise HTTPException(status_code=500, detail="API_TOKEN is not configured")
302
 
303
+ if not authorization:
304
+ raise HTTPException(status_code=401, detail="Missing Authorization header")
305
 
306
+ expected = f"Bearer {API_TOKEN}"
 
 
 
 
 
307
 
308
+ if authorization != expected:
309
+ raise HTTPException(status_code=403, detail="Invalid API token")
 
310
 
 
311
 
312
+ @app.get("/api/health")
313
+ def health():
314
+ return {
315
+ "status": "ok",
316
+ "service": "postgresql-adminer-space"
317
+ }
318
 
 
319
 
320
+ @app.get("/api/db-health")
321
+ def db_health(authorization: str | None = Header(default=None)):
322
+ verify_token(authorization)
 
 
323
 
324
+ conn = psycopg2.connect(DATABASE_URL)
325
+ cur = conn.cursor()
326
+ cur.execute("SELECT 1;")
327
+ result = cur.fetchone()
328
+ cur.close()
329
+ conn.close()
330
 
331
+ return {
332
+ "status": "ok",
333
+ "db": result[0]
334
+ }
 
335
  ```
336
 
337
  ---
338
 
339
+ ## 23. FastAPI 示例:文件索引表
340
 
341
+ 本项目议大文件保存在 `/data/files`,数据库只保存文件索引。
342
 
343
+ 管理员可以先创建文件索引表:
344
+
345
+ ```sql
346
+ CREATE TABLE IF NOT EXISTS files (
347
+ id SERIAL PRIMARY KEY,
348
+ user_id TEXT,
349
+ filename TEXT NOT NULL,
350
+ file_path TEXT NOT NULL,
351
+ file_size BIGINT,
352
+ mime_type TEXT,
353
+ created_at TIMESTAMP DEFAULT now()
354
+ );
355
  ```
356
 
357
+ API 示例
358
+
359
+ ```python
360
+ class FileRecord(BaseModel):
361
+ user_id: str | None = None
362
+ filename: str
363
+ file_path: str
364
+ file_size: int | None = None
365
+ mime_type: str | None = None
366
+
367
+
368
+ @app.post("/api/files")
369
+ def create_file(record: FileRecord, authorization: str | None = Header(default=None)):
370
+ verify_token(authorization)
371
+
372
+ conn = psycopg2.connect(DATABASE_URL)
373
+ cur = conn.cursor()
374
+
375
+ cur.execute(
376
+ """
377
+ INSERT INTO files (user_id, filename, file_path, file_size, mime_type)
378
+ VALUES (%s, %s, %s, %s, %s)
379
+ RETURNING id, created_at;
380
+ """,
381
+ (
382
+ record.user_id,
383
+ record.filename,
384
+ record.file_path,
385
+ record.file_size,
386
+ record.mime_type,
387
+ ),
388
+ )
389
+
390
+ row = cur.fetchone()
391
+ conn.commit()
392
+
393
+ cur.close()
394
+ conn.close()
395
+
396
+ return {
397
+ "id": row[0],
398
+ "created_at": row[1],
399
+ "filename": record.filename,
400
+ "file_path": record.file_path
401
+ }
402
 
 
 
 
 
 
 
 
 
403
 
404
+ @app.get("/api/files")
405
+ def list_files(authorization: str | None = Header(default=None)):
406
+ verify_token(authorization)
407
 
408
+ conn = psycopg2.connect(DATABASE_URL)
409
+ cur = conn.cursor()
 
410
 
411
+ cur.execute(
412
+ """
413
+ SELECT id, user_id, filename, file_path, file_size, mime_type, created_at
414
+ FROM files
415
+ ORDER BY id DESC
416
+ LIMIT 100;
417
+ """
418
+ )
419
 
420
+ rows = cur.fetchall()
421
+
422
+ cur.close()
423
+ conn.close()
424
+
425
+ return [
426
+ {
427
+ "id": row[0],
428
+ "user_id": row[1],
429
+ "filename": row[2],
430
+ "file_path": row[3],
431
+ "file_size": row[4],
432
+ "mime_type": row[5],
433
+ "created_at": row[6],
434
+ }
435
+ for row in rows
436
+ ]
437
  ```
438
 
439
  ---
440
 
441
+ ## 24. FastAPI 示例:受控插入数据
 
 
442
 
443
+ 可以为不同业务创建专门接口,而不是开放通用 SQL。
 
 
444
 
445
+ 例如创建一个 RSS 文章表
446
 
447
  ```sql
448
+ CREATE TABLE IF NOT EXISTS rss_articles (
449
+ id SERIAL PRIMARY KEY,
450
+ source TEXT,
451
+ title TEXT NOT NULL,
452
+ url TEXT UNIQUE,
453
+ summary TEXT,
454
+ published_at TIMESTAMP,
455
+ created_at TIMESTAMP DEFAULT now()
456
+ );
457
  ```
458
 
459
+ API 示例
460
+
461
+ ```python
462
+ class ArticleRecord(BaseModel):
463
+ source: str | None = None
464
+ title: str
465
+ url: str
466
+ summary: str | None = None
467
+ published_at: str | None = None
468
+
469
+
470
+ @app.post("/api/rss/articles")
471
+ def create_article(
472
+ article: ArticleRecord,
473
+ authorization: str | None = Header(default=None)
474
+ ):
475
+ verify_token(authorization)
476
+
477
+ conn = psycopg2.connect(DATABASE_URL)
478
+ cur = conn.cursor()
479
+
480
+ cur.execute(
481
+ """
482
+ INSERT INTO rss_articles (source, title, url, summary, published_at)
483
+ VALUES (%s, %s, %s, %s, %s)
484
+ ON CONFLICT (url) DO UPDATE SET
485
+ title = EXCLUDED.title,
486
+ summary = EXCLUDED.summary,
487
+ published_at = EXCLUDED.published_at
488
+ RETURNING id;
489
+ """,
490
+ (
491
+ article.source,
492
+ article.title,
493
+ article.url,
494
+ article.summary,
495
+ article.published_at,
496
+ ),
497
+ )
498
+
499
+ row = cur.fetchone()
500
+ conn.commit()
501
+
502
+ cur.close()
503
+ conn.close()
504
+
505
+ return {
506
+ "id": row[0],
507
+ "status": "saved"
508
+ }
509
  ```
510
 
511
+ ---
512
 
513
+ ## 25. 不推荐开放通用 SQL API
 
 
514
 
515
+ 不推荐提供如下接口
516
 
517
+ ```text
518
+ POST /api/sql
519
  ```
520
 
521
+ 并允许外部用户直接提交任意 SQL
522
 
523
+ ```json
524
+ {
525
+ "sql": "DROP TABLE users;"
526
+ }
527
  ```
528
 
529
+ 这非常危险,可能导致:
 
 
530
 
531
+ ```text
532
+ 1. 数据库被删除
533
+ 2. 数据泄露
534
+ 3. 权限绕过
535
+ 4. SQL 注入
536
+ 5. 系统被恶意占用
537
+ ```
538
 
539
+ 如果确实需要管理级 SQL API,只建议管理员内部使用,并且必须同时满足
540
 
541
  ```text
542
+ 1. 强 API Token
543
+ 2. IP 白名单
544
+ 3. Cloudflare Access
545
+ 4. 请求日志
546
+ 5. 限制 SQL 类型
547
+ 6. 禁止 DROP / TRUNCATE / ALTER 等危险语句
548
  ```
549
 
550
+ ---
551
 
552
+ ## 26. API 服务依赖安装
 
 
 
553
 
554
+ 如果增加 FastAPI API 服务,Dockerfile 需要额外安装 Python 依赖。
 
 
 
555
 
556
+ 可以在 Dockerfile 中增加
557
 
558
+ ```dockerfile
559
+ RUN apt-get update && apt-get install -y --no-install-recommends \
560
+ python3 \
561
+ python3-pip \
562
+ && rm -rf /var/lib/apt/lists/*
563
 
564
+ RUN pip3 install --break-system-packages \
565
+ fastapi \
566
+ uvicorn \
567
+ psycopg2-binary \
568
+ pydantic
569
+ ```
570
 
571
+ 或者使用 `requirements.txt`:
572
 
573
+ ```txt
574
+ fastapi
575
+ uvicorn
576
+ psycopg2-binary
577
+ pydantic
578
+ ```
579
 
580
+ 然后在 Dockerfile 中加入
581
 
582
+ ```dockerfile
583
+ COPY requirements.txt /requirements.txt
584
+ RUN pip3 install --break-system-packages -r /requirements.txt
 
 
 
 
 
 
 
 
585
  ```
586
 
587
  ---
588
 
589
+ ## 27. 同时运行 Adminer 和 API 服务
590
+
591
+ 如果当前 Space 同时需要 Adminer 和 API 服务,可以让 FastAPI 作为主 Web 服务,并把 Adminer 放到 `/adminer.php`。
592
 
593
+ 更简单的方式是继续让 Adminer 使用 `7860`,API 另外用内部端口。但 Hugging Face Space 通常只对外暴露一个 Web 端口,所以更推荐后续改成
594
 
595
  ```text
596
+ 7860:
597
+ / API 首页
598
+ /docs FastAPI 文档
599
+ /api/... API 接口
600
+ /adminer Adminer 管理入口
 
 
 
601
  ```
602
 
603
+ 如果暂时只需要 Adminer,可以增加 API 服务。
604
+
605
+ 如果需要 API 服务,建议后续把启动方式升级为:
606
 
607
  ```text
608
+ Nginx / Caddy / FastAPI 静态代理
609
+ ├── /api
610
+ └── /adminer
 
 
611
  ```
612
 
613
  ---
614
 
615
+ ## 28. 外部项目如何调用 API
616
 
617
+ 示例调用健康检查接口。
618
 
619
+ ```bash
620
+ curl -X GET "https://your-space-name.hf.space/api/db-health" \
621
+ -H "Authorization: Bearer replace_with_random_api_token"
622
  ```
623
 
624
+ 示例创建文件索引。
625
 
626
+ ```bash
627
+ curl -X POST "https://your-space-name.hf.space/api/files" \
628
+ -H "Authorization: Bearer replace_with_random_api_token" \
629
+ -H "Content-Type: application/json" \
630
+ -d '{
631
+ "user_id": "user_001",
632
+ "filename": "image_001.png",
633
+ "file_path": "/data/files/image_001.png",
634
+ "file_size": 245891,
635
+ "mime_type": "image/png"
636
+ }'
637
  ```
638
 
639
+ 示例写入 RSS 文章
640
+
641
+ ```bash
642
+ curl -X POST "https://your-space-name.hf.space/api/rss/articles" \
643
+ -H "Authorization: Bearer replace_with_random_api_token" \
644
+ -H "Content-Type: application/json" \
645
+ -d '{
646
+ "source": "example-rss",
647
+ "title": "Example Article",
648
+ "url": "https://example.com/article-1",
649
+ "summary": "This is an example article.",
650
+ "published_at": "2026-05-30T00:00:00"
651
+ }'
652
+ ```
653
 
654
  ---
655
 
656
+ ## 29. 推荐的生产访问方式
657
 
658
+ 公开 Space 推荐增加一层访问保护
659
 
660
  ```text
661
+ Cloudflare Access
662
+
663
+ Hugging Face Space
664
+
665
+ FastAPI API Token
666
+
667
+ PostgreSQL 项目用户
668
  ```
669
 
670
+ 最终安全层级
671
 
672
+ ```text
673
+ 第一层:Cloudflare Access / OAuth / 登录白名单
674
+ 第二层:API Token
675
+ 第三层:PostgreSQL 项目账号
676
+ 第四层:database / schema 权限隔离
677
+ 第五层:定期备份
678
  ```
679
 
680
+ ---
681
+
682
+ ## 30. 管理员操作流程总结
683
 
684
+ 管理员首次部署后,建议按以下步骤操作
685
 
686
  ```text
687
+ 1. 设置 POSTGRES_PASSWORD
688
+ 2. 挂载 /data 存储
689
+ 3. 启动 Space
690
+ 4. 登录 Adminer
691
+ 5. 创建项目 database
692
+ 6. 创建项目 user
693
+ 7. 授权 database 和 schema 权限
694
+ 8. 创建业务表
695
+ 9. 配置 API_TOKEN
696
+ 10. 如果需要,对外开放 FastAPI 接口
697
+ 11. 外部项目通过 API Token 调用接口
698
+ 12. 定期检查 /data/backups 备份文件
699
+ ```
700
+
701
+ 推荐不要让外部项目直接获得 `admin` 账号,也不要让外部用户直接连接 PostgreSQL 5432 端口。