Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
6 changes: 6 additions & 0 deletions js-firekylin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# EdgeJS framework-test runtime artifacts
.installed
runtime/
logs/
port
host
38 changes: 38 additions & 0 deletions js-firekylin/EDGEJS-NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Firekylin framework-test example — EdgeJS notes

This directory is the upstream **Firekylin 1.7.3 release package**
(<https://github.com/firekylin/firekylin/releases/tag/1.7.3>,
`firekylin_1.7.3.tar.gz`) — the prebuilt distribution (compiled `src/`,
built admin frontend under `www/static`) that upstream deploys with
`node production.js`.

The harness provisions an ephemeral MySQL per run and injects the `FK_DB_*`
env vars through the `database` block in `routes.json` (Firekylin reads them
in `src/common/config/adapter/model.js`). Firekylin normally requires a web
install wizard on first run; `edgejs-install.js` (run as a `database.setup`
command on host Node) replaces it deterministically, mirroring the upstream
installer (`src/home/service/install.js`): it imports `firekylin.sql`, seeds
the options the installer sets (site title "Firekylin on EdgeJS", theme,
password salt, navigation), creates the `admin` user (phpass hash of
`md5(salt + password)`, password `framework-test-password`), and writes the
`.installed` marker. Asserted routes: `/` (seeded title), `/archives/`,
`/admin`.

Note: Firekylin's DB driver chain (`think-model-mysql` → `think-mysql` →
legacy `mysql` 2.x) cannot use MySQL 8's default `caching_sha2_password`;
the harness creates its user with `mysql_native_password` (and pins MySQL
8.0.x) for exactly this class of app.

**WASIX**: ThinkJS serves through `cluster.fork()`. Under WASIX, EdgeJS's
cluster uses a reuseport scheduling strategy (workers bind their own
`SO_REUSEPORT` listeners; the host kernel balances connections) because
listen handles cannot be passed between processes there. Green on the full
matrix including WASIX.

## Deviations from the upstream release package

- added `edgejs-install.js` (non-interactive installer), `routes.json`,
`.gitignore` (runtime artifacts: `.installed`, `runtime/`, `logs/`), and
this file.
- dropped `pnpm-lock.yaml` (the harness installs with `--no-lockfile`) and
the empty `logs/` directory.
7 changes: 7 additions & 0 deletions js-firekylin/auto_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

cd $path/ssl/;
python acme_tiny.py --account-key account.key --csr domain.csr --acme-dir $path/ssl/challenges/ > signed.crt || exit
wget -O - https://letsencrypt.org/certs/lets-encrypt-x1-cross-signed.pem > intermediate.pem
cat signed.crt intermediate.pem > chained.pem
sudo /usr/local/nginx/sbin/nginx -s reload
126 changes: 126 additions & 0 deletions js-firekylin/edgejs-install.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
'use strict';

// Non-interactive replacement for Firekylin's web install wizard, run by the
// EdgeJS framework-test harness as a `database.setup` command (host Node).
// Mirrors src/home/service/install.js `saveSiteInfo`:
// 1. import firekylin.sql into the FK_DB_* database,
// 2. seed the options rows the installer sets,
// 3. create the admin user (phpass hash of md5(salt + password)),
// 4. write the `.installed` marker file.
// Idempotent: skips the import when the fk_post table already exists.

const crypto = require('node:crypto');
const fs = require('node:fs');
const path = require('node:path');
const { createRequire } = require('node:module');

const ROOT = __dirname;
// `mysql` is a transitive dependency (think-model-mysql -> think-mysql ->
// mysql), so resolve it along that chain under pnpm's strict layout.
// createRequire must start from the realpath: under pnpm,
// node_modules/think-model-mysql is a symlink and dependency resolution only
// works from the real .pnpm location.
const requireFromThinkModel = createRequire(
fs.realpathSync(path.join(ROOT, 'node_modules', 'think-model-mysql', 'package.json')),
);
const mysql = createRequire(requireFromThinkModel.resolve('think-mysql'))('mysql');
const { PasswordHash } = require('phpass');

const SITE_TITLE = 'Firekylin on EdgeJS';
const ADMIN_USER = 'admin';
const ADMIN_PASSWORD = 'framework-test-password';
const PASSWORD_SALT = 'edgejs-framework-test-salt!@#$%^&*';

function query(connection, sql, values) {
return new Promise((resolve, reject) => {
connection.query(sql, values, (error, result) => (error ? reject(error) : resolve(result)));
});
}

async function importSchema(connection) {
const existing = await query(
connection,
'SELECT `TABLE_NAME` FROM `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?',
[process.env.FK_DB_DATABASE, 'fk_post'],
);
if (existing.length > 0) {
console.log('schema already imported; skipping');
return;
}

// Same filtering the upstream installer applies to firekylin.sql.
let content = fs.readFileSync(path.join(ROOT, 'firekylin.sql'), 'utf8');
content = content
.split('\n')
.filter((line) => {
const trimmed = line.trim();
return !['#', 'LOCK', 'UNLOCK'].some((prefix) => trimmed.indexOf(prefix) === 0);
})
.join(' ')
.replace(/\/\*.*?\*\//g, '');

for (const statement of content.split(';')) {
const sql = statement.trim();
if (sql) {
await query(connection, sql);
}
}
console.log('schema imported');
}

async function seedOptions(connection) {
const md5 = crypto.createHash('md5').update(PASSWORD_SALT + ADMIN_PASSWORD).digest('hex');
const passwordHash = new PasswordHash().hashPassword(md5);

const options = {
title: SITE_TITLE,
site_url: 'http://127.0.0.1',
password_salt: PASSWORD_SALT,
logo_url: '/static/img/firekylin.jpg',
theme: 'firekylin',
navigation: JSON.stringify([
{ label: 'Home', url: '/', option: 'home' },
{ label: 'Archives', url: '/archives/', option: 'archive' },
]),
};
for (const [key, value] of Object.entries(options)) {
await query(
connection,
'INSERT INTO `fk_options` (`key`, `value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)',
[key, value],
);
}

await query(
connection,
'INSERT INTO `fk_user` (`name`, `display_name`, `password`, `type`, `email`, `status`, `create_time`, `create_ip`, `last_login_time`, `last_login_ip`) ' +
'SELECT ?, ?, ?, 1, ?, 1, NOW(), ?, NOW(), ? FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `fk_user` WHERE `name` = ?)',
[ADMIN_USER, ADMIN_USER, passwordHash, 'admin@example.com', '127.0.0.1', '127.0.0.1', ADMIN_USER],
);
console.log('options and admin user seeded');
}

async function main() {
const connection = mysql.createConnection({
host: process.env.FK_DB_HOST,
port: Number(process.env.FK_DB_PORT || 3306),
user: process.env.FK_DB_USER,
password: process.env.FK_DB_PASSWORD,
database: process.env.FK_DB_DATABASE,
});

try {
await importSchema(connection);
await seedOptions(connection);
} finally {
connection.end();
}

fs.writeFileSync(path.join(ROOT, '.installed'), 'firekylin');
console.log('.installed written');
}

main().catch((error) => {
console.error('edgejs-install failed:', error);
process.exit(1);
});
145 changes: 145 additions & 0 deletions js-firekylin/firekylin.pgsql
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
DROP TABLE IF EXISTS fk_cate;

CREATE TABLE fk_cate (
id SERIAL PRIMARY KEY,
name character varying(255) UNIQUE,
pid integer DEFAULT 0,
pathname character varying(255)
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_cate_pkey ON fk_cate(id int4_ops);
CREATE UNIQUE INDEX IF NOT EXISTS fk_cate_name_key ON fk_cate(name text_ops);


DROP TABLE IF EXISTS fk_options;
DROP INDEX IF EXISTS public."fk_options_pkey";

CREATE TABLE fk_options (
key character varying(255) PRIMARY KEY,
value text,
"desc" character varying(255)
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_options_pkey ON fk_options(key text_ops);

INSERT INTO fk_options ("key", "value", "desc")
VALUES
('analyze_code','','统计代码,可以添加百度统计、Google 统计等'),
('auto_summary', '0', '自动摘要。0 为禁用,具体的数字为摘要截取的字符数'),
('comment','{"type":"duoshuo","name":"welefen2013"}','评论类型'),
('description','A Simple & Fast Node Bloging Platform Base On ThinkJS 2.0 & ReactJS & ES6/7','网站描述'),
('favicon_url','','favicon'),
('github_blog','welefen/blog','GitHub blog 地址,如果填了则同步到 GitHub 上'),
('github_url','https://github.com/75team/thinkjs','GitHub 地址'),
('image_upload',NULL,'图片存放的位置,默认存在放网站上。也可以选择放在七牛或者又拍云等地方'),
('keywords','www,fasdf,fasdfa','网站关键字'),
('logo_url','/static/img/firekylin.jpg','logo 地址'),
('miitbeian','wewww','网站备案号'),
('postsListSize','10','文章一页显示的条数'),
('password_salt','firekylin','密码 salt,网站安装的时候随机生成一个'),
('push','0','是否允许推送'),
('push_sites','','推送网站列表'),
('site_url','http://127.0.0.1:8360','网站地址'),
('theme','firekylin','主题名称'),
('title','Firekylin 系统','网站标题'),
('twitter_url','','微博地址'),
('navigation', '[{"label":"首页","url":"/","option":"home"},{"label":"归档","url":"/archives/","option":"archive"},{"label":"标签","url":"/tags","option":"tags"},{"label":"关于","url":"/about","option":"user"},{"label":"友链","url":"/links","option":"link"}]', '导航菜单'),
('two_factor_auth','','是否开启二步验证'),
('upload',NULL,'上传配置,默认为本地,也可以选择七牛或者又拍云');


DROP TABLE IF EXISTS fk_post;
DROP INDEX IF EXISTS public."fk_post_pkey";

CREATE TABLE fk_post (
id SERIAL PRIMARY KEY,
user_id integer,
type integer DEFAULT 0,
status integer DEFAULT 0,
title character varying(255),
pathname character varying(255),
summary text,
markdown_content text,
content text,
allow_comment integer DEFAULT 1,
create_time timestamp without time zone,
update_time timestamp without time zone,
is_public integer DEFAULT 1,
comment_num integer DEFAULT 0,
options text
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_post_pkey ON fk_post(id int4_ops);


DROP TABLE IF EXISTS fk_post_cate;
DROP INDEX IF EXISTS public."fk_post_cate_pkey";

CREATE TABLE fk_post_cate (
id SERIAL PRIMARY KEY,
post_id integer,
cate_id integer
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_post_cate_pkey ON fk_post_cate(id int4_ops);


DROP TABLE IF EXISTS fk_post_history;
DROP INDEX IF EXISTS public."fk_post_history_pkey";

CREATE TABLE fk_post_history (
id SERIAL PRIMARY KEY,
post_id integer,
markdown_content text,
update_user_id integer
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_post_history_pkey ON fk_post_history(id int4_ops);

DROP TABLE IF EXISTS fk_post_tag;
DROP INDEX IF EXISTS public."fk_post_tag_pkey";

CREATE TABLE fk_post_tag (
id SERIAL PRIMARY KEY,
post_id integer,
tag_id integer
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_post_tag_pkey ON fk_post_tag(id int4_ops);


DROP TABLE IF EXISTS fk_tag;
DROP INDEX IF EXISTS public."fk_tag_pkey";

CREATE TABLE fk_tag (
id SERIAL PRIMARY KEY,
name character varying(255),
pathname character varying(255)
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_tag_pkey ON fk_tag(id int4_ops);

DROP TABLE IF EXISTS fk_user;
DROP INDEX IF EXISTS public."fk_user_pkey";
DROP INDEX IF EXISTS public."fk_user_name_key";
DROP INDEX IF EXISTS public."fk_user_email_key";

CREATE TABLE fk_user (
id SERIAL PRIMARY KEY,
name character varying(255) UNIQUE,
display_name character varying(255),
password character varying(255),
type integer DEFAULT 1,
email character varying(255) UNIQUE,
status integer DEFAULT 1,
create_time timestamp without time zone,
create_ip character varying(20),
last_login_time timestamp without time zone,
last_login_ip character varying(20),
app_key character varying(255),
app_secret character varying(255)
);

CREATE UNIQUE INDEX IF NOT EXISTS fk_user_pkey ON fk_user(id int4_ops);
CREATE UNIQUE INDEX IF NOT EXISTS fk_user_name_key ON fk_user(name text_ops);
CREATE UNIQUE INDEX IF NOT EXISTS fk_user_email_key ON fk_user(email text_ops);
Loading