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 reporter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type ReportData struct {
Hostname string `json:"hostname"`
Timeout int `json:"timeout"` // if service receive after timeout second, its means client are offline
Payload utils.ReportDataPayload `json:"payload"`
Secret *string `json:"secret,omitempty"` // optional secret for server status
}

var (
Expand All @@ -29,6 +30,7 @@ var (
Interval = flag.Int("interval", 5.0, "Input the INTERVAL, seconed")
IsVnstat = flag.Bool("vnstat", false, "Use vnstat for traffic statistics, linux only")
Verbose = flag.Bool("verbose", false, "Enable verbose logging to show full payload content")
Secret = flag.String("secret", "", "The server status secret, optional")
)

var version = "1.0.0"
Expand Down Expand Up @@ -77,6 +79,7 @@ func main() {
Hostname: hostname,
Timeout: interval * 10,
Payload: utils.GetReportDataPaylod(interval, *IsVnstat),
Secret: Secret,
}

if *Mode == "udp" {
Expand Down
6 changes: 5 additions & 1 deletion scripts/start-tianji-container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ pnpm start:docker &
sleep 10

# Start reporter with default workspace
/usr/local/bin/tianji-reporter --url "http://localhost:12345" --workspace "clnzoxcy10001vy2ohi4obbi0" --name "tianji-container" > /dev/null &
if [ -n "$SERVER_STATUS_SECRET" ]; then
/usr/local/bin/tianji-reporter --url "http://localhost:12345" --workspace "clnzoxcy10001vy2ohi4obbi0" --name "tianji-container" --secret "$SERVER_STATUS_SECRET" > /dev/null &
else
/usr/local/bin/tianji-reporter --url "http://localhost:12345" --workspace "clnzoxcy10001vy2ohi4obbi0" --name "tianji-container" > /dev/null &
fi

# Wait for any process to exit
wait -n
Expand Down
2 changes: 1 addition & 1 deletion src/client/components/server/AddServerStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const AddServerStep: React.FC = React.memo(() => {
}
});

const command = `./tianji-reporter --url ${window.location.origin} --workspace ${workspaceId}`;
const command = `./tianji-reporter --url ${window.location.origin} --workspace ${workspaceId} [--secret <your-secret>]`;

return (
<Steps
Expand Down
27 changes: 23 additions & 4 deletions src/server/model/serverStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createSubscribeInitializer, subscribeEventBus } from '../ws/shared.js';
import { isServerOnline } from '@tianji/shared';
import { getCacheManager } from '../cache/index.js';
import { logger } from '../utils/logger.js';
import { env } from '../utils/env.js';

// Helper function to get cache key for server map
function getServerMapCacheKey(workspaceId: string): string {
Expand Down Expand Up @@ -64,7 +65,7 @@ async function getServerHistoryFromCache(
const cachedValue = await cacheManager.get(key);
if (cachedValue) {
try {
return JSON.parse(String(cachedValue));
return JSON.parse(String(cachedValue));
} catch (err) {
logger.error('[ServerStatus] Error parsing cached history:', err);
return [];
Expand Down Expand Up @@ -98,7 +99,7 @@ export async function recordServerStatus(
info: ServerStatusInfo,
requestContext: ServerStatusRequestContext = {}
) {
const { workspaceId, name, hostname, timeout, payload } = info;
const { workspaceId, name, hostname, timeout, payload, secret } = info;

if (!workspaceId || !name || !hostname) {
console.warn(
Expand All @@ -108,6 +109,14 @@ export async function recordServerStatus(
return;
}

if (env.serverStatusSecret && env.serverStatusSecret !== secret) {
console.warn(
'[ServerStatus] secret mismatch, request will be ignore',
info
);
return;
}

// Get current server map from cache
const serverMap = await getServerMapFromCache(workspaceId);

Expand Down Expand Up @@ -182,9 +191,19 @@ export async function getServerCount(workspaceId: string): Promise<number> {
return Object.keys(serverMap).length;
}

export async function getServerStatusHistory(
export async function getPublicServerStatusHistory(
workspaceId: string,
name: string
): Promise<ServerStatusInfo[]> {
return await getServerHistoryFromCache(workspaceId, name);
const serverStatus = await getServerHistoryFromCache(workspaceId, name);
return serverStatus.map((item: ServerStatusInfo) => {
// we remove sensitive datas
const { secret, ...rest } = item;
const { top_cpu_processes, top_memory_processes, docker, ...restPayload } =
rest.payload;
return {
...rest,
payload: restPayload,
};
});
}
1 change: 1 addition & 0 deletions src/server/router/serverStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ serverStatusRouter.post(
header('x-tianji-report-version').isSemVer(),
body('workspaceId').isString(),
body('name').isString(),
body('secret').optional().isString(),
body('hostname').isString(),
body('timeout').optional().isInt(),
body('payload').isObject()
Expand Down
16 changes: 13 additions & 3 deletions src/server/trpc/routers/serverStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
} from '../trpc.js';
import {
clearOfflineServerStatus,
getServerStatusHistory,
getPublicServerStatusHistory,
getServerMapFromCache,
} from '../../model/serverStatus.js';
import { OPENAPI_TAG } from '../../utils/const.js';
Expand Down Expand Up @@ -57,7 +57,17 @@ export const serverStatusRouter = router({
const filteredServerMap: Record<string, any> = {};
serverNames.forEach((name) => {
if (serverMap[name]) {
filteredServerMap[name] = serverMap[name];
const { secret, ...rest } = serverMap[name];
const {
top_cpu_processes,
top_memory_processes,
docker,
...restPayload
} = rest.payload;
filteredServerMap[name] = {
...rest,
payload: restPayload,
};
}
});

Expand All @@ -84,6 +94,6 @@ export const serverStatusRouter = router({
)
.query(async ({ input }) => {
const { workspaceId, name } = input;
return getServerStatusHistory(workspaceId, name);
return getPublicServerStatusHistory(workspaceId, name);
}),
});
1 change: 1 addition & 0 deletions src/server/utils/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const env = {
debugAIFeature: checkEnvTrusty(process.env.DEBUG_AI_FEATURE), // debug ai feature
debugInsights: checkEnvTrusty(process.env.DEBUG_INSIGHTS) || isDev, // debug insights
enableFunctionWorker: checkEnvTrusty(process.env.ENABLE_FUNCTION_WORKER),
serverStatusSecret: process.env.SERVER_STATUS_SECRET, // an optional secret for server monitoring
};

export function checkEnvTrusty(env: string | undefined): boolean {
Expand Down
1 change: 1 addition & 0 deletions src/types/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ServerStatusInfo {
timeout: number;
updatedAt: number;
payload: ServerStatusInfoPayload & ServerStatusRequestContext;
secret?: string; // optional secret
}

export interface ServerStatusInfoPayload {
Expand Down
112 changes: 59 additions & 53 deletions website/docs/install/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,86 +8,92 @@ Tianji supports various environment variables to customize its behavior. You can

## Basic Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `PORT` | Server port | `12345` | `3000` |
| `JWT_SECRET` | Secret for JWT tokens | Random Text | `your-secret-key` |
| `ALLOW_REGISTER` | Enable user registration | `false` | `true` |
| `ALLOW_OPENAPI` | Enable OpenAPI access | `true` | `false` |
| `WEBSITE_ID` | Website identifier | - | `your-website-id` |
| `DISABLE_AUTO_CLEAR` | Disable automatic data cleanup | `false` | `true` |
| `DISABLE_ACCESS_LOGS` | Disable access logs | `false` | `true` |
| `DB_DEBUG` | Enable database debugging | `false` | `true` |
| Variable | Description | Default | Example |
| --------------------- | ------------------------------ | ----------- | ----------------- |
| `PORT` | Server port | `12345` | `3000` |
| `JWT_SECRET` | Secret for JWT tokens | Random Text | `your-secret-key` |
| `ALLOW_REGISTER` | Enable user registration | `false` | `true` |
| `ALLOW_OPENAPI` | Enable OpenAPI access | `true` | `false` |
| `WEBSITE_ID` | Website identifier | - | `your-website-id` |
| `DISABLE_AUTO_CLEAR` | Disable automatic data cleanup | `false` | `true` |
| `DISABLE_ACCESS_LOGS` | Disable access logs | `false` | `true` |
| `DB_DEBUG` | Enable database debugging | `false` | `true` |

## Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `DISABLE_ACCOUNT` | Disable account-based authentication | `false` | `true` |
| `AUTH_SECRET` | Authentication secret | MD5 of JWT secret | `your-auth-secret` |
| `AUTH_RESTRICT_EMAIL` | Restrict registration to specific email domains | - | `@example.com` |
| Variable | Description | Default | Example |
| --------------------- | ----------------------------------------------- | ----------------- | ------------------ |
| `DISABLE_ACCOUNT` | Disable account-based authentication | `false` | `true` |
| `AUTH_SECRET` | Authentication secret | MD5 of JWT secret | `your-auth-secret` |
| `AUTH_RESTRICT_EMAIL` | Restrict registration to specific email domains | - | `@example.com` |

### Email Authentication and Email Invitation

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `EMAIL_SERVER` | SMTP server for email | - | `smtp://user:[email protected]:587` |
| `EMAIL_FROM` | Email sender address | - | `[email protected]` |
| Variable | Description | Default | Example |
| -------------- | --------------------- | ------- | --------------------------------------- |
| `EMAIL_SERVER` | SMTP server for email | - | `smtp://user:[email protected]:587` |
| `EMAIL_FROM` | Email sender address | - | `[email protected]` |

### GitHub Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_GITHUB_ID` | GitHub OAuth client ID | - | `your-github-client-id` |
| `AUTH_GITHUB_SECRET` | GitHub OAuth client secret | - | `your-github-client-secret` |
| Variable | Description | Default | Example |
| -------------------- | -------------------------- | ------- | --------------------------- |
| `AUTH_GITHUB_ID` | GitHub OAuth client ID | - | `your-github-client-id` |
| `AUTH_GITHUB_SECRET` | GitHub OAuth client secret | - | `your-github-client-secret` |

### Google Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_GOOGLE_ID` | Google OAuth client ID | - | `your-google-client-id` |
| `AUTH_GOOGLE_SECRET` | Google OAuth client secret | - | `your-google-client-secret` |
| Variable | Description | Default | Example |
| -------------------- | -------------------------- | ------- | --------------------------- |
| `AUTH_GOOGLE_ID` | Google OAuth client ID | - | `your-google-client-id` |
| `AUTH_GOOGLE_SECRET` | Google OAuth client secret | - | `your-google-client-secret` |

### Custom OAuth/OIDC Authentication

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AUTH_CUSTOM_ID` | Custom OAuth/OIDC client ID | - | `your-custom-client-id` |
| `AUTH_CUSTOM_SECRET` | Custom OAuth/OIDC client secret | - | `your-custom-client-secret` |
| `AUTH_CUSTOM_NAME` | Custom provider name | `Custom` | `Enterprise SSO` |
| `AUTH_CUSTOM_TYPE` | Authentication type | `oidc` | `oauth` |
| `AUTH_CUSTOM_ISSUER` | OIDC issuer URL | - | `https://auth.example.com` |
| Variable | Description | Default | Example |
| -------------------- | ------------------------------- | -------- | --------------------------- |
| `AUTH_CUSTOM_ID` | Custom OAuth/OIDC client ID | - | `your-custom-client-id` |
| `AUTH_CUSTOM_SECRET` | Custom OAuth/OIDC client secret | - | `your-custom-client-secret` |
| `AUTH_CUSTOM_NAME` | Custom provider name | `Custom` | `Enterprise SSO` |
| `AUTH_CUSTOM_TYPE` | Authentication type | `oidc` | `oauth` |
| `AUTH_CUSTOM_ISSUER` | OIDC issuer URL | - | `https://auth.example.com` |

## AI Features

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `SHARED_OPENAI_API_KEY` | OpenAI API key | - | `your-openai-api-key` |
| `SHARED_OPENAI_BASE_URL` | Custom OpenAI API URL | - | `https://api.openai.com/v1` |
| `SHARED_OPENAI_MODEL_NAME` | OpenAI model to use | `gpt-4o` | `gpt-3.5-turbo` |
| `DEBUG_AI_FEATURE` | Debug AI features | `false` | `true` |
| Variable | Description | Default | Example |
| -------------------------- | --------------------- | -------- | --------------------------- |
| `SHARED_OPENAI_API_KEY` | OpenAI API key | - | `your-openai-api-key` |
| `SHARED_OPENAI_BASE_URL` | Custom OpenAI API URL | - | `https://api.openai.com/v1` |
| `SHARED_OPENAI_MODEL_NAME` | OpenAI model to use | `gpt-4o` | `gpt-3.5-turbo` |
| `DEBUG_AI_FEATURE` | Debug AI features | `false` | `true` |

## Sandbox Configuration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `USE_VM2` | Use VM2 for sandbox execution | `false` | `true` |
| `SANDBOX_MEMORY_LIMIT` | Memory limit for sandbox (MB) | `16` | `32` |
| `PUPPETEER_EXECUTABLE_PATH` | Custom path to Puppeteer executable | - | `/usr/bin/chromium` |
| Variable | Description | Default | Example |
| --------------------------- | ----------------------------------- | ------- | ------------------- |
| `USE_VM2` | Use VM2 for sandbox execution | `false` | `true` |
| `SANDBOX_MEMORY_LIMIT` | Memory limit for sandbox (MB) | `16` | `32` |
| `PUPPETEER_EXECUTABLE_PATH` | Custom path to Puppeteer executable | - | `/usr/bin/chromium` |

## Maps Integration

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `AMAP_TOKEN` | AMap (Gaode) API token | - | `your-amap-token` |
| `MAPBOX_TOKEN` | Mapbox API token | - | `your-mapbox-token` |
| Variable | Description | Default | Example |
| -------------- | ---------------------- | ------- | ------------------- |
| `AMAP_TOKEN` | AMap (Gaode) API token | - | `your-amap-token` |
| `MAPBOX_TOKEN` | Mapbox API token | - | `your-mapbox-token` |

## Telemetry

| Variable | Description | Default | Example |
| --- | --- | --- | --- |
| `DISABLE_ANONYMOUS_TELEMETRY` | Disable anonymous telemetry | `false` | `true` |
| `CUSTOM_TRACKER_SCRIPT_NAME` | Custom tracker script name | - | `custom-tracker.js` |
| Variable | Description | Default | Example |
| ----------------------------- | --------------------------- | ------- | ------------------- |
| `DISABLE_ANONYMOUS_TELEMETRY` | Disable anonymous telemetry | `false` | `true` |
| `CUSTOM_TRACKER_SCRIPT_NAME` | Custom tracker script name | - | `custom-tracker.js` |

## Server Status Reporting

| Variable | Description | Default | Example |
| ---------------------- | ---------------------------------- | ------- | --------------------------- |
| `SERVER_STATUS_SECRET` | Secret for server status reporting | - | `your-server-status-secret` |

## Setting Environment Variables

Expand Down