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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ dist-ssr
*.sln
*.sw?
/.dev.vars

# rinapp docker data
data/
5 changes: 3 additions & 2 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"dev": "bunx --bun vite",
"build": "tsc && vite build",
"start": "vite preview --port=${RIN_APP_UI_PORT:-5173} --host=${RIN_APP_UI_IP:-localhost}",
"check": "tsc",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
Expand Down Expand Up @@ -33,7 +34,7 @@
"reactjs-popup": "^2.0.6",
"remark-github-blockquote-alert": "^1.2.1",
"remixicon": "^4.2.0",
"rin-server": "workspace:rin-server@latest",
"rin-server": "file:../../server",
"typescript-cookie": "^1.0.6",
"wouter": "^3.1.3",
"yet-another-react-lightbox": "^3.21.1"
Expand All @@ -52,4 +53,4 @@
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
}
74 changes: 74 additions & 0 deletions deploy/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
FROM node:22.21.1-trixie AS base
# Why not use bun? https://github.com/cloudflare/workers-sdk/issues/7196

# build image
# FROM base AS builder

# ARG TARGETARCH

# WORKDIR /workdir

# RUN apt-get update && apt-get install -y curl

# # downloads latest workerd release
# RUN curl -LO \
# $(curl -s https://api.github.com/repos/cloudflare/workerd/releases/latest \
# | grep -wo "https.*linux-$([ $TARGETARCH = "arm64" ] && echo "arm64" || echo "64").gz")

# RUN ls -la && gunzip workerd*.gz && mv workerd* workerd && chmod +x workerd

# # copies runtime libraries
# RUN mkdir lib && \
# cp /lib/*-linux-gnu/libdl.so.2 lib/libdl.so.2 && \
# cp /lib/*-linux-gnu/librt.so.1 lib/librt.so.1

# install
FROM base AS install

WORKDIR /app

COPY .. /app

RUN cd /app && npm install

RUN npm run b

# container basis workerd
# FROM busybox:glibc

# ENV MINIFLARE_WORKERD_PATH="/usr/bin/workerd"

# COPY --from=builder /workdir/workerd /usr/bin/workerd
# COPY --from=builder /workdir/lib /usr/lib

# WORKDIR /worker

# ENTRYPOINT [ "workerd" ]

# container image
FROM base

# use workerd
# COPY --from=builder /workdir/workerd /usr/bin/workerd
# COPY --from=builder /workdir/lib /usr/lib
# ENV MINIFLARE_WORKERD_PATH="/usr/bin/workerd"

# copy assets
COPY --from=install /app/client /app/client
COPY --from=install /app/docs /app/docs
COPY --from=install /app/deploy /app/deploy
COPY --from=install /app/scripts /app/scripts
COPY --from=install /app/server /app/server
COPY --from=install /app/node_modules /app/node_modules
COPY --from=install /app/package.json /app/package.json
COPY --from=install /app/turbo.json /app/turbo.json
COPY --from=install /app/wrangler.example.toml /app/wrangler.example.toml
COPY --from=install /app/LICENSE /app/LICENSE
COPY --from=install /app/README.md /app/README.md
COPY --from=install /app/.dev.example.vars /app/.dev.example.vars

ENV RIN_APP_SRV_IP=0.0.0.0 RIN_APP_UI_IP=0.0.0.0

WORKDIR /app

ENTRYPOINT [ "/bin/sh", "/app/deploy/entrypoint.sh" ]
27 changes: 27 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!--
* @Author: Bin
* @Date: 2026-01-11
* @FilePath: /Rin/deploy/README.md
-->

# 使用 Docker 运行 Rin

1、首先 clone 代码仓库 `git clone https://github.com/openRin/Rin.git`

2、参考文档 [环境变量列表](../docs/ENV.md) 创建并配置文件 `.dev.vars` 和 `wrangler.toml`

```bash
# 构建 image
docker build . -f ./deploy/Dockerfile -t rinapp

# 启动服务
docker run -d \
-p 11498:11498 \
-p 5173:5173 \
-v .dev.vars:/app/.dev.vars \
-v ./wrangler.toml:/app/wrangler.toml \
-v ./data/wrangler:/app/.wrangler \
rinapp:latest
```

> `http://127.0.0.1:11498` 为部署的后端地址,`http://127.0.0.1:5173` 为部署的前端地址。数据存储目录为 `./data` 文件夹。可根据自己需求调整 docker 运行参数。
12 changes: 12 additions & 0 deletions deploy/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh
###
# @Author: Bin
# @Date: 2026-01-12
# @FilePath: /Rin/deploy/entrypoint.sh
###

# migrator data
node --experimental-strip-types ./scripts/dev-migrator.ts

# server start
npm run start
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
"dev:client": "bun --filter './client' dev",
"dev:server": "bun wrangler dev --port 11498",
"dev:cron": "bun wrangler dev --port 11498 --test-scheduled",
"start": "turbo start",
"start:client": "npm run start -w client",
"start:server": "npm run start -w server",
"start:cron": "npm run start:cron -w server",
"check": "turbo check",
"cf-deploy": "bun scripts/migrator.ts",
"b": "turbo build",
Expand Down Expand Up @@ -49,5 +53,6 @@
"remark-gfm": "^4.0.0",
"remark-math": "^6.0.0",
"remark-rehype": "^11.1.0"
}
}
},
"packageManager": "[email protected]"
}
9 changes: 7 additions & 2 deletions scripts/dev-migrator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { execSync } from 'child_process';
import { fixTopField, getMigrationVersion, isInfoExist, updateMigrationVersion } from './fix-top-field';
import { fixTopField, getMigrationVersion, isInfoExist, updateMigrationVersion } from './fix-top-field.ts';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const DB_NAME = "rin";
const SQL_DIR = path.join(__dirname, '..', 'server', 'sql');
Expand Down Expand Up @@ -29,7 +33,8 @@ for (const file of sqlFiles) {
const filePath = path.join(SQL_DIR, file);
// Run the migration
try {
execSync(`bunx wrangler d1 execute ${DB_NAME} --local --file "${filePath}"`, { stdio: 'inherit' });
const isBun = typeof Bun !== "undefined";
execSync(`${isBun ? "bunx": "npx"} wrangler d1 execute ${DB_NAME} --local --file "${filePath}"`, { stdio: 'inherit' });
console.log(`Executed ${file}`);
} catch (error) {
console.error(`Failed to execute ${file}: ${error}`);
Expand Down
59 changes: 49 additions & 10 deletions scripts/fix-top-field.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,46 @@
import { $ } from "bun"
import { exec } from "node:child_process";
import { promisify } from "node:util";

const execAsync = promisify(exec);

/**
* 跨平台的 Shell 执行器
* 自动识别 Bun 或 Node 环境
*/
async function runCommand(cmd: string): Promise<any> {
// 检测是否在 Bun 环境中
const isBun = typeof Bun !== "undefined";

if (isBun) {
// 动态导入 Bun 的 $ 以避免在 Node 中解析错误
const { $ } = await import("bun");
// 使用 bunx,并静默执行获取 JSON
return await $`${{ raw: cmd }}`.quiet().json();
} else {
// Node 环境逻辑
// 将 bunx 替换为 npx (如果命令里包含 bunx)
const nodeCmd = cmd.replace(/^bunx /, "npx ");
try {
const { stdout } = await execAsync(nodeCmd);
return JSON.parse(stdout);
} catch (e: any) {
// 模拟 Bun 的报错行为或处理空输出
if (e.stdout) return JSON.parse(e.stdout);
throw e;
}
}
}

export async function fixTopField(typ: 'local' | 'remote', db: string, isInfoExistResult: boolean) {
if (!isInfoExistResult) {
console.log("Legacy database, check top field")
const result = await $`bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT name FROM pragma_table_info('feeds') WHERE name='top'"`.quiet().json()
const cmd = `bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT name FROM pragma_table_info('feeds') WHERE name='top'"`
const result = await runCommand(cmd);

if (result[0].results.length === 0) {
console.log("Adding top field to feeds table")
await $`bunx wrangler d1 execute ${db} --${typ} --json --command "ALTER TABLE feeds ADD COLUMN top INTEGER DEFAULT 0"`.quiet()
const addColCmd = `bunx wrangler d1 execute ${db} --${typ} --json --command "ALTER TABLE feeds ADD COLUMN top INTEGER DEFAULT 0"`
await runCommand(addColCmd);
} else {
console.log("Top field already exists in feeds table")
}
Expand All @@ -16,8 +50,10 @@ export async function fixTopField(typ: 'local' | 'remote', db: string, isInfoExi
}

export async function isInfoExist(typ: 'local' | 'remote', db: string) {
const result = await $`bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT name FROM sqlite_master WHERE type='table' AND name='info'"`.quiet().json()
if (result[0].results.length === 0) {
const cmd = `bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT name FROM sqlite_master WHERE type='table' AND name='info'"`
const result = await runCommand(cmd);

if (result[0]?.results?.length === 0) {
console.log("info table not exists")
return false
} else {
Expand All @@ -32,13 +68,15 @@ export async function getMigrationVersion(typ: 'local' | 'remote', db: string) {
console.log("Legacy database, migration_version not exists")
return -1
}
const result = await $`bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT value FROM info WHERE key='migration_version'"`.quiet().json()
if (result[0].results.length === 0) {
const cmd = `bunx wrangler d1 execute ${db} --${typ} --json --command "SELECT value FROM info WHERE key='migration_version'"`
const result = await runCommand(cmd);

if (result[0]?.results?.length === 0) {
console.log("migration_version not exists")
return -1
} else {
console.log("migration_version:", result[0].results[0].value)
return parseInt(result[0].results[0].value)
console.log("migration_version:", result[0]?.results?.[0]?.value)
return parseInt(result[0]?.results?.[0].value)
}
}

Expand All @@ -48,6 +86,7 @@ export async function updateMigrationVersion(typ: 'local' | 'remote', db: string
console.log("info table not exists, skip update migration_version")
throw new Error("info table not exists")
}
await $`bunx wrangler d1 execute ${db} --${typ} --json --command "UPDATE info SET value='${version}' WHERE key='migration_version'"`.quiet()
const cmd = `bunx wrangler d1 execute ${db} --${typ} --json --command "UPDATE info SET value='${version}' WHERE key='migration_version'"`
await runCommand(cmd);
console.log("Updated migration_version to", version)
}
4 changes: 3 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"cf:deploy": "wrangler deploy",
"dev": "bun wrangler dev --port 11498",
"dev:cron": "bun wrangler dev --port 11498 --test-scheduled #see https://developers.cloudflare.com/workers/configuration/cron-triggers/#test-cron-triggers",
"start": "wrangler dev --port=${RIN_APP_SRV_PORT:-11498} --ip=${RIN_APP_SRV_IP:-localhost}",
"start:cron": "wrangler dev --port=${RIN_APP_SRV_PORT:-11498} --ip=${RIN_APP_SRV_IP:-localhost} --test-scheduled",
"cf-typegen": "wrangler types",
"db:gen": "bun drizzle-kit generate",
"preview": "wrangler pages dev dist --compatibility-date=2023-12-20"
Expand Down Expand Up @@ -47,4 +49,4 @@
"trustedDependencies": [
"es5-ext"
]
}
}
36 changes: 27 additions & 9 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@
"$schema": "https://turbo.build/schema.json",
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
"dependsOn": [
"^build"
],
"outputs": [
"dist/**"
]
},
"dev": {
"cache": false,
"persistent": true
},
"start": {
"cache": false,
"persistent": true
},
"t": {
"cache": false,
"dependsOn": ["^g"]
"dependsOn": [
"^g"
]
},
"g": {
"cache": false,
"dependsOn": ["^db:gen"]
"cache": false,
"dependsOn": [
"^db:gen"
]
},
"db:gen": {
"cache": false
Expand All @@ -24,16 +36,22 @@
"cache": false
},
"format:check": {
"dependsOn": ["^format:check"]
"dependsOn": [
"^format:check"
]
},
"format:write": {
"dependsOn": ["^format:write"]
"dependsOn": [
"^format:write"
]
},
"cf:deploy": {
"cache": false
},
"check": {
"dependsOn": ["^check"]
"dependsOn": [
"^check"
]
}
}
}
}