geoflow-content-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GEOFlow Content Automation

GEOFlow 内容自动化

Skill by ara.so — Daily 2026 Skills collection.
GEOFlow is an open-source PHP/PostgreSQL system for automated GEO/SEO content production. It chains model configuration, material management, task scheduling, draft review, and front-end publishing into a single pipeline. It supports any OpenAI-compatible API, runs on Docker Compose, and exposes both a REST API and a CLI.

ara.so开发的Skill — 2026每日技能合集。
GEOFlow 是一款基于PHP/PostgreSQL的开源系统,用于自动化GEO/SEO内容生产。它将模型配置、素材管理、任务调度、草稿审核和前端发布整合为一条完整流水线。它支持所有兼容OpenAI的API,可通过Docker Compose运行,并提供REST API和CLI两种交互方式。

Installation

安装

Docker (Recommended)

Docker(推荐)

bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
cp .env.example .env
bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
cp .env.example .env

Edit .env — set APP_SECRET_KEY, SITE_URL, and DB credentials

编辑.env — 设置APP_SECRET_KEY、SITE_URL和数据库凭据

vi .env
vi .env

Start web + postgres + scheduler + worker

启动web + postgres + scheduler + worker服务

docker compose --profile scheduler up -d --build
docker compose --profile scheduler up -d --build

Front-end

前端页面

Admin panel

管理面板

Local PHP Server

本地PHP服务器

bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow

export DB_DRIVER=pgsql
export DB_HOST=127.0.0.1
export DB_PORT=5432
export DB_NAME=geo_system
export DB_USER=geo_user
export DB_PASSWORD=geo_password
export APP_SECRET_KEY=$(openssl rand -hex 32)
export SITE_URL=http://localhost:8080

php -S localhost:8080 router.php
open http://localhost:8080/geo_admin/
Requirements: PHP 7.4+, extensions
pdo_pgsql
and
curl
, PostgreSQL instance.

bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow

export DB_DRIVER=pgsql
export DB_HOST=127.0.0.1
export DB_PORT=5432
export DB_NAME=geo_system
export DB_USER=geo_user
export DB_PASSWORD=geo_password
export APP_SECRET_KEY=$(openssl rand -hex 32)
export SITE_URL=http://localhost:8080

php -S localhost:8080 router.php
open http://localhost:8080/geo_admin/
要求: PHP 7.4+,需安装
pdo_pgsql
curl
扩展,且已有PostgreSQL实例。

Environment Variables

环境变量

dotenv
undefined
dotenv
undefined

.env (copy from .env.example)

.env(从.env.example复制)

HOST_PORT=18080 SITE_URL=http://localhost:18080 APP_SECRET_KEY=$APP_SECRET_KEY # 32+ char random string — never hardcode CRON_INTERVAL=60 # Scheduler poll interval in seconds TZ=Asia/Shanghai
HOST_PORT=18080 SITE_URL=http://localhost:18080 APP_SECRET_KEY=$APP_SECRET_KEY # 32位以上随机字符串 — 切勿硬编码 CRON_INTERVAL=60 # 调度器轮询间隔(秒) TZ=Asia/Shanghai

Database

数据库配置

DB_DRIVER=pgsql DB_HOST=postgres DB_PORT=5432 DB_NAME=geo_system DB_USER=geo_user DB_PASSWORD=$DB_PASSWORD # Set via environment, not hardcoded

Generate a secure key:

```bash
openssl rand -hex 32
DB_DRIVER=pgsql DB_HOST=postgres DB_PORT=5432 DB_NAME=geo_system DB_USER=geo_user DB_PASSWORD=$DB_PASSWORD # 通过环境变量设置,请勿硬编码

生成安全密钥:

```bash
openssl rand -hex 32

or

php -r "echo bin2hex(random_bytes(32));"

---
php -r "echo bin2hex(random_bytes(32));"

---

Default Credentials

默认凭据

After first boot, log in at
/geo_admin/
with:
  • Username:
    admin
  • Password:
    admin888
Change both immediately and rotate
APP_SECRET_KEY
before any public deployment.

首次启动后,使用以下凭据登录
/geo_admin/
  • 用户名:
    admin
  • 密码:
    admin888
请立即修改这两项凭据,并在公开部署前更新
APP_SECRET_KEY

Docker Services

Docker服务

ServicePurposeProfile
web
Front-end + admin HTTPalways
postgres
PostgreSQL databasealways
scheduler
Scans tasks, writes job queue
scheduler
worker
Calls AI API, generates content
scheduler
bash
undefined
服务用途配置文件
web
前端+管理后台HTTP服务始终运行
postgres
PostgreSQL数据库始终运行
scheduler
扫描任务,写入任务队列
scheduler
worker
调用AI API,生成内容
scheduler
bash
undefined

Web only (no generation)

仅启动Web服务(不生成内容)

docker compose up -d
docker compose up -d

Full stack

启动完整服务栈

docker compose --profile scheduler up -d
docker compose --profile scheduler up -d

Logs

查看日志

docker compose logs -f worker docker compose logs -f scheduler
docker compose logs -f worker docker compose logs -f scheduler

Restart a single service

重启单个服务

docker compose restart worker
docker compose restart worker

Stop everything

停止所有服务

docker compose --profile scheduler down

---
docker compose --profile scheduler down

---

Content Generation Pipeline

内容生成流水线

Admin: configure model + prompts + materials
Create Task (title library, model, prompt, image library, publish rules)
Scheduler (bin/cron.php) → writes to job_queue table
Worker (bin/worker.php) → calls AI API → generates article body
Optional: insert images, build SEO meta
Article enters draft → review → publish states
Front-end renders article + SEO/OG tags

管理员:配置模型+提示词+素材
创建任务(标题库、模型、提示词、图片库、发布规则)
调度器(bin/cron.php)→ 写入job_queue表
Worker(bin/worker.php)→ 调用AI API → 生成文章正文
可选步骤:插入图片、构建SEO元数据
文章进入草稿→审核→发布状态
前端渲染文章+SEO/OG标签

Admin Workflow

管理工作流

1. Add an AI Model

1. 添加AI模型

Navigate to AI配置中心 → AI模型管理 and fill in:
  • API Base URL (e.g.
    https://api.openai.com/v1
    )
  • Model ID (e.g.
    gpt-4o
    )
  • API Key (stored encrypted; reference
    $AI_API_KEY
    in your env)
导航至 AI配置中心 → AI模型管理 并填写以下内容:
  • API基础URL(例如
    https://api.openai.com/v1
  • 模型ID(例如
    gpt-4o
  • API密钥(加密存储;在环境变量中引用
    $AI_API_KEY

2. Create Materials

2. 创建素材

  • 标题库 — Title pool the task draws from
  • 图片库 — Image pool for article illustrations
  • 知识库 — Reference knowledge injected into prompts
  • 提示词模板 — Prompt templates with placeholders
  • 标题库 — 任务可选用的标题池
  • 图片库 — 文章配图的图片池
  • 知识库 — 注入提示词的参考知识
  • 提示词模板 — 带占位符的提示词模板

3. Create a Task

3. 创建任务

Navigate to 任务管理 → 新建任务:
Title Library  → selects article titles
Model          → which AI model to call
Prompt         → which prompt template
Image Library  → optional illustration set
Publish Mode   → draft | auto-publish
Schedule       → immediate | cron-based
导航至 任务管理 → 新建任务
标题库  → 选择文章标题来源
模型          → 调用的AI模型
提示词         → 使用的提示词模板
图片库  → 可选的配图集合
发布模式   → 草稿 | 自动发布
调度方式       → 立即执行 | 定时任务

4. Monitor Execution

4. 监控执行状态

  • 任务管理 — Task status, retry controls
  • 文章管理 — Draft/review/published states
  • 审核中心 — Review queue for human approval

  • 任务管理 — 任务状态、重试控制
  • 文章管理 — 草稿/审核/发布状态
  • 审核中心 — 人工审核队列

CLI (
bin/geoflow
)

CLI(
bin/geoflow

The CLI is the programmatic interface used by automation scripts and the companion skill
yaojingang/yao-geo-skills
.
bash
undefined
CLI是自动化脚本和配套技能
yaojingang/yao-geo-skills
使用的编程接口。
bash
undefined

List available commands

查看可用命令

php bin/geoflow help
php bin/geoflow help

Check task status

检查任务状态

php bin/geoflow task:status --task-id=<id>
php bin/geoflow task:status --task-id=<id>

Create a task

创建任务

php bin/geoflow task:create
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft
php bin/geoflow task:create
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft

Upload an article draft

上传文章草稿

php bin/geoflow article:create
--title="Article Title"
--body="<markdown content>"
--status=draft
php bin/geoflow article:create
--title="Article Title"
--body="<markdown content>"
--status=draft

Review and publish an article

审核并发布文章

php bin/geoflow article:review --article-id=<id> --action=approve php bin/geoflow article:publish --article-id=<id>
php bin/geoflow article:review --article-id=<id> --action=approve php bin/geoflow article:publish --article-id=<id>

Run scheduler manually (one cycle)

手动运行调度器(单次循环)

php bin/cron.php
php bin/cron.php

Run worker manually (processes one job then exits)

手动运行Worker(处理一个任务后退出)

php bin/worker.php --once

---
php bin/worker.php --once

---

REST API (
/api/v1
)

REST API(
/api/v1

All endpoints require Bearer token authentication.
所有接口均需Bearer令牌认证。

Generate an API Token

生成API令牌

bash
php bin/api/create_token.php --name="my-integration"
bash
php bin/api/create_token.php --name="my-integration"

Outputs token — store in $GEOFLOW_API_TOKEN

输出令牌 — 存储到$GEOFLOW_API_TOKEN

undefined
undefined

Common Endpoints

常用接口

bash
BASE=http://localhost:18080/api/v1
TOKEN=$GEOFLOW_API_TOKEN
bash
BASE=http://localhost:18080/api/v1
TOKEN=$GEOFLOW_API_TOKEN

List tasks

列出所有任务

curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks"
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks"

Get task detail

获取任务详情

curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks/<id>"
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks/<id>"

Create task

创建任务

curl -X POST
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"
curl -X POST
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"

List articles

列出所有文章

curl -H "Authorization: Bearer $TOKEN" "$BASE/articles"
curl -H "Authorization: Bearer $TOKEN" "$BASE/articles"

Publish article

发布文章

curl -X POST
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"
curl -X POST
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"

Check job queue

查看待处理任务队列

curl -H "Authorization: Bearer $TOKEN" "$BASE/jobs?status=pending"

---
curl -H "Authorization: Bearer $TOKEN" "$BASE/jobs?status=pending"

---

PHP Code Examples

PHP代码示例

Connect to the Database (using GEOFlow's own layer)

连接数据库(使用GEOFlow自带的数据库层)

php
<?php
// includes/db_support.php provides the connection helper
require_once __DIR__ . '/includes/db_support.php';

$pdo = get_db_connection(); // Returns a PDO instance

// Always use prepared statements
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = :status ORDER BY created_at DESC LIMIT 10");
$stmt->execute([':status' => 'published']);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);
php
<?php
// includes/db_support.php提供连接助手
require_once __DIR__ . '/includes/db_support.php';

$pdo = get_db_connection(); // 返回PDO实例

// 始终使用预处理语句
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = :status ORDER BY created_at DESC LIMIT 10");
$stmt->execute([':status' => 'published']);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);

Call the Article Service

调用文章服务

php
<?php
require_once __DIR__ . '/includes/article_service.php';

// Create a draft
$articleId = create_article([
    'title'       => 'My Article Title',
    'body'        => '## Introduction\n\nContent here...',
    'status'      => 'draft',
    'task_id'     => 42,
    'seo_title'   => 'My Article Title | Site Name',
    'seo_desc'    => 'Short description for search engines.',
]);

// Approve and publish
approve_article($articleId);
publish_article($articleId);
php
<?php
require_once __DIR__ . '/includes/article_service.php';

// 创建草稿
$articleId = create_article([
    'title'       => 'My Article Title',
    'body'        => '## Introduction\n\nContent here...',
    'status'      => 'draft',
    'task_id'     => 42,
    'seo_title'   => 'My Article Title | Site Name',
    'seo_desc'    => 'Short description for search engines.',
]);

// 审核并发布
approve_article($articleId);
publish_article($articleId);

Trigger the AI Engine Directly

直接触发AI引擎

php
<?php
require_once __DIR__ . '/includes/ai_engine.php';

// Execute a single task job (task_id, job_id)
$result = run_task_job(taskId: 5, jobId: 101);

if ($result['success']) {
    echo "Article created: " . $result['article_id'];
} else {
    echo "Error: " . $result['error'];
}
php
<?php
require_once __DIR__ . '/includes/ai_engine.php';

// 执行单个任务作业(task_id, job_id)
$result = run_task_job(taskId: 5, jobId: 101);

if ($result['success']) {
    echo "Article created: " . $result['article_id'];
} else {
    echo "Error: " . $result['error'];
}

Queue a Job Manually

手动添加任务到队列

php
<?php
require_once __DIR__ . '/includes/job_queue_service.php';

// Claim next available job (used by worker)
$job = claim_next_job();

if ($job) {
    try {
        // ... process the job ...
        complete_job($job['id']);
    } catch (Exception $e) {
        fail_job($job['id'], $e->getMessage());
        // Eligible jobs will be retried by scheduler
    }
}

// Manually enqueue a task
enqueue_task_jobs(taskId: 5);
php
<?php
require_once __DIR__ . '/includes/job_queue_service.php';

// 获取下一个可用任务(Worker使用)
$job = claim_next_job();

if ($job) {
    try {
        // ... 处理任务 ...
        complete_job($job['id']);
    } catch (Exception $e) {
        fail_job($job['id'], $e->getMessage());
        // 可重试任务将由调度器重新处理
    }
}

// 手动将任务加入队列
enqueue_task_jobs(taskId: 5);

Custom Prompt Template in PHP

PHP自定义提示词模板

php
<?php
// Prompt templates support placeholder substitution
$template = "You are an SEO writer. Write a 600-word article about: {{title}}\n\nBackground knowledge:\n{{knowledge}}";

$prompt = str_replace(
    ['{{title}}', '{{knowledge}}'],
    [$articleTitle, $knowledgeBaseContent],
    $template
);

// Pass to AI service
require_once __DIR__ . '/includes/ai_service.php';
$response = call_ai_model(modelId: 2, prompt: $prompt);
echo $response['content'];

php
<?php
// 提示词模板支持占位符替换
$template = "You are an SEO writer. Write a 600-word article about: {{title}}\n\nBackground knowledge:\n{{knowledge}}";

$prompt = str_replace(
    ['{{title}}', '{{knowledge}}'],
    [$articleTitle, $knowledgeBaseContent],
    $template
);

// 传递给AI服务
require_once __DIR__ . '/includes/ai_service.php';
$response = call_ai_model(modelId: 2, prompt: $prompt);
echo $response['content'];

Directory Reference

目录结构参考

GEOFlow/
├── index.php                 Front-end article list
├── article.php               Article detail page (SEO + OG)
├── router.php                Local dev router for php -S
├── admin/                    All admin UI pages
├── api/v1/index.php          REST API single entry point
├── bin/
│   ├── geoflow               CLI entrypoint
│   ├── cron.php              Scheduler (run every CRON_INTERVAL seconds)
│   └── worker.php            AI generation worker (long-running)
├── includes/
│   ├── config.php            Global config + constants
│   ├── database.php          Front-end data access
│   ├── ai_engine.php         Core generation orchestration
│   ├── ai_service.php        OpenAI-compatible HTTP client
│   ├── job_queue_service.php Claim / complete / fail / retry
│   ├── task_service.php      Task CRUD
│   ├── article_service.php   Article lifecycle
│   └── api_auth.php          Bearer token auth
└── docs/                     Deployment + API + FAQ docs

GEOFlow/
├── index.php                 前端文章列表页
├── article.php               文章详情页(含SEO + OG标签)
├── router.php                本地开发环境php -S的路由文件
├── admin/                    所有管理后台UI页面
├── api/v1/index.php          REST API统一入口
├── bin/
│   ├── geoflow               CLI入口文件
│   ├── cron.php              调度器(每隔CRON_INTERVAL秒运行一次)
│   └── worker.php            AI生成任务Worker(常驻进程)
├── includes/
│   ├── config.php            全局配置+常量
│   ├── database.php          前端数据访问层
│   ├── ai_engine.php         核心生成编排逻辑
│   ├── ai_service.php        兼容OpenAI的HTTP客户端
│   ├── job_queue_service.php 任务队列的认领/完成/失败/重试逻辑
│   ├── task_service.php      任务CRUD操作
│   ├── article_service.php   文章生命周期管理
│   └── api_auth.php          Bearer令牌认证
└── docs/                     部署+API+FAQ文档

Common Patterns

常见使用模式

Auto-publish without review

无需审核自动发布

When creating a task, set
publish_mode = auto
. The worker will call
publish_article()
immediately after generation, skipping the review queue.
创建任务时设置
publish_mode = auto
,Worker生成文章后会立即调用
publish_article()
,跳过审核队列。

Batch retry failed jobs

批量重试失败任务

bash
undefined
bash
undefined

Via CLI

通过CLI

php bin/geoflow job:retry-failed --task-id=<id>
php bin/geoflow job:retry-failed --task-id=<id>

Via database (emergency)

通过数据库(紧急情况)

psql $DATABASE_URL -c "UPDATE job_queue SET status='pending', attempts=0 WHERE status='failed' AND task_id=5;"
undefined
psql $DATABASE_URL -c "UPDATE job_queue SET status='pending', attempts=0 WHERE status='failed' AND task_id=5;"
undefined

Run scheduler outside Docker

在Docker外运行调度器

bash
undefined
bash
undefined

Add to crontab — runs every minute

添加到crontab — 每分钟运行一次

          • cd /path/to/GEOFlow && php bin/cron.php >> /var/log/geoflow-cron.log 2>&1
          • cd /path/to/GEOFlow && php bin/cron.php >> /var/log/geoflow-cron.log 2>&1

Or loop manually

或手动循环运行

while true; do php bin/cron.php; sleep 60; done
undefined
while true; do php bin/cron.php; sleep 60; done
undefined

Database maintenance

数据库维护

bash
php bin/db_maintenance.php --vacuum
php bin/db_maintenance.php --clean-old-jobs --days=30

bash
php bin/db_maintenance.php --vacuum
php bin/db_maintenance.php --clean-old-jobs --days=30

Troubleshooting

故障排除

SymptomLikely CauseFix
Worker generates nothingScheduler not runningStart with
--profile scheduler
or run
cron.php
pdo_pgsql
not found
Extension missingAdd
extension=pdo_pgsql
to
php.ini
or install
php-pgsql
500 on admin pagesDB not initializedLet the web container complete its entrypoint migration on first boot
AI calls return 401Wrong API keyRe-enter key in AI模型管理; check key has no leading/trailing spaces
Articles stuck in draftAuto-publish offSet
publish_mode=auto
on the task, or manually approve in 审核中心
Jobs pile up in queueWorker crashed
docker compose restart worker
; check
docker compose logs worker
CSRF token mismatchSession expiredRe-login to admin; ensure
APP_SECRET_KEY
is stable across restarts
症状可能原因解决方法
Worker无内容生成调度器未运行使用
--profile scheduler
启动服务,或手动运行
cron.php
找不到
pdo_pgsql
扩展未安装
php.ini
中添加
extension=pdo_pgsql
,或安装
php-pgsql
管理后台页面报500错误数据库未初始化等待Web容器在首次启动时完成入口脚本的数据库迁移
AI调用返回401错误API密钥错误AI模型管理中重新输入密钥;检查密钥无首尾空格
文章一直停留在草稿状态未开启自动发布在任务中设置
publish_mode=auto
,或在审核中心手动审核
任务队列堆积Worker崩溃执行
docker compose restart worker
;查看
docker compose logs worker
日志
CSRF令牌不匹配会话过期重新登录管理后台;确保
APP_SECRET_KEY
在重启后保持稳定

Useful Log Commands

实用日志命令

bash
undefined
bash
undefined

All services

查看所有服务日志

docker compose logs -f
docker compose logs -f

Only errors

仅查看错误日志

docker compose logs worker 2>&1 | grep -i error
docker compose logs worker 2>&1 | grep -i error

Scheduler activity

查看调度器活动日志

docker compose logs scheduler | tail -50
docker compose logs scheduler | tail -50

Check pending jobs in DB

查看数据库中待处理任务

docker compose exec postgres psql -U geo_user -d geo_system
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"

---
docker compose exec postgres psql -U geo_user -d geo_system
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"

---

Security Checklist

安全检查清单

  • Change default
    admin
    /
    admin888
    credentials immediately
  • Set
    APP_SECRET_KEY
    to a 32+ character random string via environment variable
  • Never commit
    .env
    — it is in
    .gitignore
    by default
  • Use HTTPS in production; set
    SITE_URL
    to your
    https://
    domain
  • Restrict
    /geo_admin/
    and
    /api/v1/
    with network-level firewall in production
  • Rotate API tokens periodically via
    php bin/api/create_token.php
  • 立即修改默认
    admin
    /
    admin888
    凭据
  • 通过环境变量设置
    APP_SECRET_KEY
    为32位以上随机字符串
  • 切勿提交
    .env
    文件 — 默认已加入
    .gitignore
  • 生产环境使用HTTPS;将
    SITE_URL
    设置为你的
    https://
    域名
  • 生产环境通过网络防火墙限制
    /geo_admin/
    /api/v1/
    的访问
  • 定期通过
    php bin/api/create_token.php
    轮换API令牌