BAID's website. Built with Next.js.
Overview · Development · Production · Environment Variables · Contribution · License
Helium is the new content management system for the website of Beijing Academy International Division (BAID) and the International School of Beijing Academy (ISBA).
Helium is based on BAID-CSClub/baid-website-next by @lihe07 and @lemonadedw. It was originally developed by Lin Donglai and is now maintained by WebArtistry @BAID.
- Everything that is displayed on the website for visitors is a
ContentEntity. AContentEntityincludes the title, content, cover images, and other metadata for something that is displayed on the website.- Each metadata type in a
ContentEntitycorresponds to four fields: a Chinese draft field, an English draft field, a Chinese published field, and an English published field. - The draft fields are what the editors for the website change, while the published fields are what is displayed on the website for visitors.
- To publish the draft fields to the published fields—a process known as "alignment"—a series of approvals must be
completed by a specified number of editors and admins, as defined by
ApprovalConfigobjects. - There are 7 types of
ContentEntityobjects, as defined in the enumEntityType:post,page,club,activity,project,course, andfaculty. EachEntityTypeis displayed differently on the frontend. Most notably, thepageEntityTypeis displayed completely differently from the otherContentEntitytypes, as it is completely customizable using a WYSIWYG Puck editor, with pre-defined, customizable components insrc/app/lib/puck/componentsfor editors to use. The otherEntityTypetypes all follow a fixed template, with limited customization supported by the Plate editor.
- Each metadata type in a
- While
ContentEntityobjects are the core of Helium, it includes many other amazing features that greatly simplify the website management process.- Integrated MCP server. Helium can be added to your agent of choice to automate all your workflows exactly the way you want it.
- AI-powered content importing. Helium supports importing content from WeChat Official Accounts, automatically
creating
posttypeContentEntityobjects from them. This feature automatically translates the Chinese content to English and sanitizes the content to remove formatting artifacts using AI models provided by Feishu Aily (which the school pays for). - Live collaboration. Live, synchronized editing is supported for both the Plate and Puck editors using Y.js and Hocuspocus.
- Feishu notifications. Helium can send approval notifications and publication notifications to appropriate users with Feishu integration.
- Comments and suggestions. Commenting and suggestions are available to mark content for review before publishing.
- Portal integration. By using LinkBAID, Helium supports logging in with Seiue LMS and Feishu accounts.
- Asset optimization. Images are automatically optimized and converted to WebP format when they are uploaded.
- Automated backups. All
ContentEntityobjects are periodically backed up to a ZIP file, which can be downloaded and restored by admins. - Umami integration. Admins can open Umami through the Studio sidebar. Helium verifies admin access and signs into a configured Umami account automatically.
To run in development:
- Copy
.env.exampleto.env. - Install Node.js, npm, and FFmpeg. Ensure that FFmpeg is available on
PATH, or setFFMPEG_PATH. - Run
npm install. - Set up a PostgreSQL database and set the
DATABASE_URIenvironment variable in.env. - Run
npm run db:deployto create the database schema. - Run
npm run db:generateto generate the Prisma client. - Run decorative-image-classifier concurrently to support WeChat content imports. This external service automatically removes decorative images from WeChat posts to enable easier editing. (This isn't strictly necessary.)
- Gain access to the production Feishu Aily app and Feishu notification apps by asking your school point of contact. (This isn't strictly necessary.)
- Run a development server of LinkBAID to support authentication or use the official LinkBAID server by informing your school point of contact.
- Fill the remaining environment variables.
- Run
npm run devto start the development server.
-
After pulling someone else's migrations, run
npm run db:migratelocally, thennpm run db:generate. -
To create your own migration, edit
prisma/schema.prismaand runnpm run db:migrate -- --name <describe_change>. Commit the schema and the entireprisma/migrations/directory together. -
On production, run
npm run db:deploy, thennpm run db:generatebefore building and restarting Helium. Monitor the messages carefully, do NOT reset the database!
To run in production:
- Copy
.env.exampleto.env. - Install Node.js, npm, and FFmpeg. Ensure that FFmpeg is available on
PATH, or setFFMPEG_PATH. - Run
npm install. - Set up a PostgreSQL database and set the
DATABASE_URIenvironment variable in.env. - For a fresh database, run
npm run db:deploy. - Run
npm run db:generateto generate the Prisma client. - Run decorative-image-classifier in the background.
You can use
pm2ortmuxto run it. - Gain access to the production LinkBAID server, Feishu Aily app, and Feishu notification apps.
- LinkBAID server: You must create a LinkBAID application with the following scopes:
basic,phone, andsms. This app must be approved before continuing. - Notification app: You will need to create an app on Feishu Open Platform with the following scopes:
im:message,im:message:send_as_bot. You must set the redirect URL to include${HOST}/studio/settings/feishu/callback. - Aily app: You must create an enterprise agent on Feishu Aily Development Platform, not Feishu Open
Platform. In the Feishu Aily Development Platform, enable tenant access in "access methods." You do not have to
set up any prompts for this agent. Then return to the Feishu Open Platform and enable the following scopes:
aily:agent_artifact:read,aily:agent_attachment:write,aily:agent_chat:read,aily:agent_chat:write,aily:agent_visibility:read,aily:data_asset:read,aily:data_asset:upload_file,aily:data_asset:write,aily:file:read.
- LinkBAID server: You must create a LinkBAID application with the following scopes:
- Set up a cron task that triggers
curl -fsS "https://example.com/api/backups/daily?key=$CRON_KEY"for automatic backups. Use the sameCRON_KEYthat you set in the environment variables. - Fill the remaining environment variables.
- Run
npm run buildto build the production server. - Using
pm2, runpm2 start npm --name helium -- run startto start the production server. You can also usepm2 saveto save the process list andpm2 startupto configure it to start on boot. - Helium runs on port 52323 by default, and the Hocuspocus collaboration server runs on port 1234 by default.
- Create a reverse proxy with Nginx as appropriate such that Nginx handles HTTPS and serves static files. An example Nginx configuration is shown below.
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/helium;
client_max_body_size 250M; # Must cover the 250 MB video upload limit
location ^~ /uploads/ {
alias /home/web/helium-baid/uploads/; # By setting up Nginx to serve files from /uploads/, we must set `UPLOAD_SERVE_PATH` to `/uploads` (users access uploaded files on `https://.../uploads/...`) and `UPLOAD_PATH` to `uploads` (uploaded files are saved to `/var/www/helium/uploads/`).
autoindex off;
add_header X-Content-Type-Options "nosniff" always;
try_files $uri =404;
}
location ^~ /collaboration { # No trailing slash!
proxy_pass http://127.0.0.1:1234/; # Match your Hocuspocus port
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
location / {
proxy_pass http://localhost:52323/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_request_buffering off;
}
}Umami runs as a separate application with its own PostgreSQL database. Follow the Umami installation instructions to install a release.
-
In Umami's directory, configure its
.envwith a separate database and the analytics subpath:DATABASE_URL=postgresql://umami:password@127.0.0.1:5432/umami BASE_PATH=/analytics HOSTNAME=127.0.0.1 PORT=13888 CLIENT_IP_HEADER=x-forwarded-for
Make sure
X-Forwarded-Foris configured properly so Umami country statistics are accurate. -
Install dependencies and build Umami, then start it with the environment-aware script:
pnpm install pnpm run build pm2 start pnpm --name umami -- run start:env pm2 save
-
Add both locations below to the same Nginx
serverblock that serves Helium. Theproxy_passvalues preserve the/analyticsprefix. Keep these locations the same as Umami'sBASE_PATHand port.location = /analytics { proxy_pass http://127.0.0.1:13888; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ^~ /analytics/ { proxy_pass http://127.0.0.1:13888; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }
-
Open
/analyticson the public website and sign into Umami. Its initial credentials areadmin/umami; change the password immediately. Create or select the Umami account that Helium admins will share. Do not use 2FA. -
In Helium's
.env, configure the relative dashboard path and that Umami account's credentials:ANALYTICS_HOST=/analytics UMAMI_USERNAME=helium-analytics UMAMI_PASSWORD=replace-with-the-account-password
Making sure that the analytics host is under the same origin of Helium itself.
-
In Umami, add the school website. Copy the tracking snippets into the "website metadata" section.
-
Setup complete!
| Name | Description |
|---|---|
DATABASE_URI |
The database URI to use. PostgreSQL is required. |
HOST |
The absolute URL where Helium is hosted. No trailing slashes. For example, http://localhost:3000 in development or https://baid.beijingacademy.com.cn in production. |
ANALYTICS_HOST |
Same-origin relative path to Umami, default /analytics. Must match Umami's build-time BASE_PATH and the Nginx locations. |
UMAMI_USERNAME |
Server-only username of the Umami account used by the admin analytics shortcut. |
UMAMI_PASSWORD |
Server-only password of that Umami account. Automatic login requires a full session token from password login. |
UPLOAD_PATH |
The directory where uploaded files are stored. In development, use public/uploads to have the Next.js development server serve uploaded files. |
UPLOAD_SERVE_PATH |
The relative URL where uploaded files are served. In development, this is uploads. |
BACKUP_PATH |
Directory for content backups. Defaults to backups in the working directory. Must not be inside UPLOAD_PATH or any other publicly served directory, because backups include unpublished drafts. |
ONELOGIN_HOST |
The location where OneLogin is hosted. No trailing slashes. |
ONELOGIN_CLIENT_ID |
OneLogin client ID. basic, phone, and sms scopes are required. |
ONELOGIN_CLIENT_SECRET |
OneLogin client secret. |
JWT_SECRET |
The JWT secret key to use. You can generate one with openssl rand -hex 32. |
FFMPEG_PATH |
Optional path to the FFmpeg executable used to generate video thumbnails. Defaults to ffmpeg on PATH. |
CRON_KEY |
Secret key required by cron-only API endpoints. Generate one with openssl rand -hex 32. |
FEISHU_CLIENT_ID |
Feishu app used for approval notifications. Starts with cli_. |
FEISHU_CLIENT_SECRET |
Feishu app used for approval notifications. |
FEISHU_AI_CLIENT_ID |
Another Feishu app used for translating and sanitizing WeChat imports. Starts with cli_. |
FEISHU_AI_CLIENT_SECRET |
Another Feishu app used for translating and sanitizing WeChat imports. |
FEISHU_AILY_AGENT_ID |
Another Feishu app used for translating and sanitizing WeChat imports. You must create an agent and paste the agent ID here. Starts with agent_. It's in the browser address bar. |
NEXT_PORT |
Port used by the production Next.js server. Defaults to 52323. |
HOCUSPOCUS_PORT |
Local port for the Plate collaboration server. Defaults to 1234. |
HOCUSPOCUS_INTERNAL_URL |
Server-only HTTP base URL for MCP editor requests to Hocuspocus, such as http://127.0.0.1:1234. Set this to bypass the reverse proxy if you set up one in production. If omitted, requests fall back to NEXT_PUBLIC_HOCUSPOCUS_URL. |
NEXT_PUBLIC_HOCUSPOCUS_URL |
Browser WebSocket URL for Plate collaboration, such as ws://192.168.1.20/collaboration/. Set this before running npm run build. |
SERVER_ACTIONS_ALLOWED_ORIGINS |
Comma-separated hosts (e.g. example.com,10.0.0.5:8523) allowed to call server actions when the browser origin differs from the Host header, such as behind a reverse proxy. Defaults to the BAID production hosts. Set this before running npm run build. |
Contribution is accepted from Beijing Academy students. All contributions are owned by Beijing Academy.
All rights reserved unless otherwise stated. Refer to LICENSE for details.
"Beijing Academy," "BAID," "Better Me, Better World," and the Beijing Academy logo are legally protected and may not be used without official authorization.