Logo
活死人の行知路

Strapi入门指南


📅 | 📝 817 字
#programming #Strapi

1前置准备

  • 安装 Node.js (用于 Strapi)
  • 安装 Go 环境 (用于自动化脚本)
  • 安装 Hugo (用于前端静态化)

2初始化工作目录与前端 (Hugo)

我们创建一个统一的工作目录来存放所有代码。

# 创建工作目录并进入
mkdir -p ~/project && cd ~/project

# 初始化 Hugo 前端项目,命名为 frontend
hugo new site frontend

cd frontend
# 创建 themes 文件夹
mkdir -p themes

# 将 PaperMod 主题克隆到 themes/PaperMod 目录下 (加上 --depth=1 可以让下载极快)
git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1
# 启用主题
echo 'theme = "PaperMod"' >> hugo.toml
# 预览
hugo server -D --bind 0.0.0.0 --port 1314

此时,你的 frontend 目录下已经有了 Hugo 的基础结构(包含 content 目录用于存放文章)。


3初始化与配置后端 (Strapi)

1. 创建并启动 Strapi 项目~/project 目录下执行:

# 创建名为 backend 的 Strapi 项目,并默认使用 SQLite 数据库快速启动,
# 我们也可以指定其他数据库,支持的数据库有 sqlite、postgres、mysql。
pnpm create strapi-app@latest backend

如果不是用默认的 SQLite,需要提前现在本地创建好其他的数据库,以便在创建项目时配置连接。

看到下面返回信息则表示安装完成。

安装完成后根据返回的提示命令 pnpm run develophttp://localhost:1337/admin 启动。

2. 注册超级管理员 打开浏览器,访问 http://localhost:1337/admin。系统会要求你创建一个管理员账号(填入用户名、邮箱、密码)。这是你的绝对控制权账号。

选择跳过:

首页展示:

3. 创建数据模型 (建表) 登录后,按照以下步骤为客户建一个“文章”表:

  • 点击左侧菜单栏的 “Content-Type Builder”

  • 点击 “Create new collection type”,在 Display name 中输入 Article,点击 Continue。

  • 依次添加三个字段(Fields):

  • Text,命名为 Title(短文本)。

  • Rich Text,命名为 Content

  • Text,命名为 Slug(短文本,用于生成网址路径,比如填 about-us)。

如果还要继续添加字段可以点击右下角的 【Add another field】,如果全部添加完成则点击【Finish】

  • 点击左上角的 “Save”,Strapi 会自动重启并生成数据库表。

4. 录入第一条测试数据

  • 点击左侧菜单的 “Content Manager”,选择 Article

  • 点击 “Create new entry”

  • 填入测试内容(Title: 测试文章, Slug: test-01, Content: 这是一篇测试文章)。

  • 点击 “Save”,然后一定要点击 “Publish”(发布)。

5. 开放 API 访问权限 (就是之前让你配置的那一步)

  • 点击左侧最下方的 “Settings”

  • 找到 “Users & Permissions Plugin” 下的 “Roles”,点击 “Public”

  • 在权限列表中找到 Article,勾选 find(获取列表)和 findOne(获取单条)。

  • 点击右上角 “Save”

测试一下:在浏览器访问 http://localhost:1337/api/articles,如果能看到一串包含你刚才录入内容的 JSON 数据,说明后端彻底搞定了。


4编写自动化监听脚本 (Golang)

这个脚本的作用是:一直在后台盯着,一旦 Strapi 有变动,它就把数据拉下来,塞给 Hugo 重新生成网页。

1. 初始化 Go 项目

cd ~/project
mkdir connector && cd connector
go mod init connector

2. 编写代码

代码片段(这里的路径已经匹配了我们在第二阶段创建的 frontend 目录):

package web

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"

	"encoding/json"
	"text/template"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func (h *StrapiHandler) RegisterRoutes(server *gin.Engine) {
	// 微信公众平台 (Official Account) 消息推送网关与管理接口
	g := server.Group("/portal")
	{
		g.POST("/webhook", h.webhookHandler) // portal 门户、入口站点需对微信服务器免登录放行
	}
}

func (h *StrapiHandler) webhookHandler(ctx *gin.Context) {
	log.Println("检测到内容更新,开始自动化构建...")
	go func() {
		if err := h.syncDataAndBuild(); err != nil {
			log.Printf("❌ 构建流程出错: %v\n", err)
		} else {
			log.Println("✅ 静态官网已成功生成至 ../frontend/public 目录!")
		}
	}()
	ctx.JSON(http.StatusOK, gin.H{"status": "building"})
}

func (h *StrapiHandler) syncDataAndBuild() error {
	// 1. 获取 Strapi 数据
	resp, err := http.Get("http://localhost:1337/api/articles")
	if err != nil {
		return fmt.Errorf("请求 Strapi 接口失败: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return fmt.Errorf("读取 Strapi 响应失败: %v", err)
	}

	// 拦截非 200 状态码(比如 403 权限未开)
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Strapi 返回异常状态码: %d, 响应体: %s", resp.StatusCode, string(body))
	}

	var result StrapiResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return fmt.Errorf("解析 JSON 失败: %v, 原始数据: %s", err, string(body))
	}

	log.Printf("成功从 Strapi 拉取到 %d 篇文章", len(result.Data))

	// 2. 写入 Hugo 目录
	postsDir := "../frontend/content/posts/"
	os.RemoveAll(postsDir)
	if err := os.MkdirAll(postsDir, 0755); err != nil {
		return fmt.Errorf("创建文章目录失败: %v", err)
	}

	tmpl, err := template.New("md").Parse(mdTemplate)
	if err != nil {
		return fmt.Errorf("解析 Markdown 模板失败: %v", err)
	}

	for _, item := range result.Data {
		// 修改这里,直接访问 item.Slug
		slug := item.Slug
		if slug == "" {
			slug = fmt.Sprintf("article-%d", item.Id)
			log.Printf("⚠️ 警告: 文章《%s》的 Slug 为空,自动重命名为: %s", item.Title, slug)
		}

		fileName := filepath.Join(postsDir, fmt.Sprintf("%s.md", slug))
		file, err := os.Create(fileName)
		if err != nil {
			log.Printf("❌ 创建文件 %s 失败: %v", fileName, err)
			continue
		}

		err = tmpl.Execute(file, item)
		if err != nil {
			log.Printf("❌ 写入模板内容至 %s 失败: %v", fileName, err)
		}
		file.Close()
	}

	// 3. 执行 Hugo 构建
	cmd := exec.Command("hugo")
	cmd.Dir = "../frontend"

	// 核心修复:捕获 Hugo 的完整输出信息,而不仅仅是退出状态码
	output, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("Hugo 构建失败: %v\nHugo 详细报错信息:\n%s", err, string(output))
	}

	return nil
}

// 彻底适配 Strapi v5 拍平后的 JSON 结构
type StrapiResponse struct {
	Data []struct {
		Id          int    `json:"id"`
		Title       string `json:"title"`
		Description string `json:"description"` // 匹配你的 JSON 里的 description
		Slug        string `json:"slug"`
		PublishedAt string `json:"publishedAt"` // 直接复用 Strapi 的发布时间,解决未来时间不渲染的问题
	} `json:"data"`
}

// 去掉 Attributes 前缀,直接渲染,并自动使用真实的发布时间
const mdTemplate = `---
title: "{{.Title}}"
slug: "{{.Slug}}"
date: {{.PublishedAt}}
---

{{.Description}}
`

3. 运行服务


5打通最后一步 (配置 Webhook)

现在 Go 监听服务已经在 8080 端口跑起来了,我们要告诉 Strapi:有内容更新时,去敲这个 8080 端口的门。

  1. 回到浏览器的 Strapi 后台。

  2. 点击左侧 “Settings”,在 Global Settings 下找到 “Webhooks”

  3. 点击 “Create new webhook”

  4. Name 随便填(例如:Notify_Frontend_to_Update)。

  5. URL 填入:http://localhost:8080/portal/webhook

  6. Events 勾选 Entry 这一行(代表新建、修改、删除文章都会触发)。

  7. 点击右上角 “Save”


6验证全链路

现在,你可以去 Strapi 后台新建一篇文章并点击发布。 切回服务器终端,你会看到 Go 脚本立刻打印出:检测到内容更新,开始自动化构建...静态官网已生成...

此时你去 ~/project/frontend/public 目录下看,最新的 .html 静态页面已经自动生成好了。企业客户的官网瞬间完成了更新。


7高级使用

你能敏锐地意识到现在使用的 “超级管理员账号”, 我们“不能把超级管理员账号给客户”。

超级管理员(Super Admin)拥有修改数据库结构(Content-Type Builder)、配置 Webhook、甚至删除系统的最高权限。如果把这个账号给到不懂技术的客户,他们一旦误点,整个系统可能瞬间瘫痪。

在 Strapi 中,分配企业客户权限的核心思路是:利用 Strapi 的“管理员角色(Admin Panel Roles)”机制,为客户创建一个只能“发文章、删文章”,但绝对看不到“底层配置”的受限账号。

以下是手把手的配置指南和定制开发建议:

7.1区分两个“用户系统”(防坑必看)

在配置之前,必须要理清 Strapi 里的两个“用户组”(很多人在这里踩坑):

  1. Users & Permissions Plugin(API 用户): 这是给前台网站的用户用的(比如你的官网如果需要用户注册登录、评论文章)。
  2. Administration Panel(后台管理员): 这是给后台操作面板的用户用的(比如你、企业老板、企业文员)。

你要给客户开账号,必须去第二种(Administration Panel)里操作!


7.2为企业客户配置专属权限(实操步骤)

Strapi 免费版(Community Edition)默认提供了三个后台角色:Super Admin(你用的)、Editor(编辑)和 Author(作者)。我们通常把 Editor(编辑) 角色改造为交付给企业老板/文员使用的角色。

第一步:定制 Editor(编辑)角色的权限

  1. 登录你的超级管理员账号。

  2. 点击左侧底部的 Settings(设置)。

  3. 在左侧菜单找到 Administration Panel 下的 Roles(角色)。

  4. 点击 Editor 进入编辑页面。

  5. 核心裁剪操作:

  • Plugins (插件权限): 把里面所有的勾全部取消掉(特别是 Content-Type Builder 和 Webhooks,绝对不能让客户碰)。

  • Settings (设置权限): 确保所有的勾都是取消状态

  • Collection Types (集合权限): 找到你创建的 Article(或者叫 文章)。勾选 CreateReadUpdateDeletePublish

  1. 点击右上角的 Save

经过这步操作,Editor 角色就被彻底“阉割”成了一个纯粹的“内容打字员”。

第二步:为客户创建专属账号

  1. 依然在 Settings -> Administration Panel 下,点击 Users(用户)。

  2. 点击右上角的 Invite new user(邀请新用户)或 Add new user(添加新用户)。

  3. 填写客户的姓名和邮箱(可以是伪造的邮箱,因为你可以直接发密码给他们)。

  4. Role(角色)选择: 必须选择刚才配置好的 Editor

  5. 复制顶部的激活链接让用户自己或者我们先为其设置简单密码后,把这个账号和密码发给你的企业客户。

见证奇迹的时刻: 你可以开一个浏览器的无痕窗口,用刚才给客户建的账号登录一下 Strapi。你会发现,左侧那些让人头晕的 Content-Type BuilderSettings 菜单全部消失了!客户登录进来,左侧干净得只剩下“文章管理”和“媒体库”。这才是商业交付该有的极简体验。


7.3关于开源版 (免费版) 的限制与对策

这里必须诚实地告诉你一个 Strapi 的商业策略: 在 Strapi 免费版(Community Edition)中,你只能修改默认的这三个角色(Editor、Author),不能新建自定义角色(比如新建一个叫“A公司管理员”的角色)。新建角色是企业付费版(Enterprise Edition)的功能。


7.4进一步的“定制开发”建议

权限剥离干净后,这个后台依然叫 Strapi,为了让它更像你为客户定制的专属系统,你可以在代码里进行简单的“白标(White-labeling)”替换:

在你的 Strapi 项目目录中,找到 src/admin/app.js(或 app.tsx),你可以修改以下内容:

export default {
  config: {
    // 替换登录页的语言
    locales: ['zh-Hans'],
    // 替换后台左上角的文字
    translations: {
      'zh-Hans': {
        'Auth.form.welcome.title': '欢迎使用企业内容管理系统',
        'Auth.form.welcome.subtitle': '登录您的专属后台',
        'app.components.LeftMenu.navbrand.title': 'XX企业后台',
      },
    },
    // 替换登录页和左上角的 Logo (将图片放在 src/admin/extensions/ 目录下)
    auth: {
      logo: './extensions/my-company-logo.png',
    },
    menu: {
      logo: './extensions/my-company-icon.png',
    },
  },
  bootstrap(app) {
    console.log(app);
  },
};

修改完这个文件后,需要在终端重新执行 pnpm buildpnpm develop,后台就会彻底变成你自己的品牌。