mirror of
https://github.com/whyour/qinglong.git
synced 2026-08-05 16:25:04 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c17dbdc28f | |||
| da8089aaac | |||
| c063619763 |
@@ -261,153 +261,3 @@ jobs:
|
||||
- name: Image digest
|
||||
run: |
|
||||
echo ${{ steps.docker_build_310.outputs.digest }}
|
||||
|
||||
build-debian:
|
||||
needs: build-static
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: "8.3.1"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Setup timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
with:
|
||||
timezoneLinux: Asia/Shanghai
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta-debian
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
${{ github.repository }}
|
||||
ghcr.io/${{ github.repository }}
|
||||
# generate Docker tags based on the following events/attributes
|
||||
# nightly, master, pr-2, 1.2.3, 1.2, 1
|
||||
flavor: |
|
||||
latest=false
|
||||
suffix=-debian,onlatest=true
|
||||
tags: |
|
||||
type=schedule,pattern=nightly
|
||||
type=edge
|
||||
type=ref,event=pr
|
||||
type=ref,event=branch,enable=${{ github.ref != format('refs/heads/{0}', 'master') }}
|
||||
type=raw,value=debian,enable=${{ github.ref == format('refs/heads/{0}', 'master') }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push Debian
|
||||
id: docker_build_debian
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
MAINTAINER=${{ github.repository_owner }}
|
||||
QL_BRANCH=${{ github.ref_name }}
|
||||
SOURCE_COMMIT=${{ github.sha }}
|
||||
network: host
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
context: .
|
||||
file: ./docker/debian.Dockerfile
|
||||
push: true
|
||||
tags: ${{ steps.meta-debian.outputs.tags }}
|
||||
labels: ${{ steps.meta-debian.outputs.labels }}
|
||||
cache-from: type=registry,ref=whyour/qinglong:cache-debian
|
||||
cache-to: type=registry,ref=whyour/qinglong:cache-debian,mode=max
|
||||
|
||||
- name: Image digest
|
||||
run: |
|
||||
echo ${{ steps.docker_build_debian.outputs.digest }}
|
||||
|
||||
build-debian-310:
|
||||
if: ${{ github.ref_name == 'master' }}
|
||||
needs: build-static
|
||||
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: "8.3.1"
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Setup timezone
|
||||
uses: szenius/set-timezone@v2.0
|
||||
with:
|
||||
timezoneLinux: Asia/Shanghai
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and push Debian python3.10
|
||||
id: docker_build_debian_310
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
build-args: |
|
||||
MAINTAINER=${{ github.repository_owner }}
|
||||
QL_BRANCH=${{ github.ref_name }}
|
||||
SOURCE_COMMIT=${{ github.sha }}
|
||||
network: host
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
context: .
|
||||
file: ./docker/debian-310.Dockerfile
|
||||
push: true
|
||||
tags: whyour/qinglong:debian-python3.10
|
||||
cache-from: type=registry,ref=whyour/qinglong:cache-debian-python3.10
|
||||
cache-to: type=registry,ref=whyour/qinglong:cache-debian-python3.10,mode=max
|
||||
|
||||
- name: Image digest
|
||||
run: |
|
||||
echo ${{ steps.docker_build_debian_310.outputs.digest }}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
FROM python:3.10-slim-bookworm AS builder
|
||||
COPY package.json .npmrc pnpm-lock.yaml /tmp/build/
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends nodejs npm git ca-certificates \
|
||||
&& npm i -g pnpm@8.3.1 pm2 ts-node \
|
||||
&& cd /tmp/build \
|
||||
&& pnpm install --prod \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM python:3.10-slim-bookworm
|
||||
|
||||
ARG QL_MAINTAINER="whyour"
|
||||
LABEL maintainer="${QL_MAINTAINER}"
|
||||
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
|
||||
ARG QL_BRANCH=develop
|
||||
ARG PYTHON_SHORT_VERSION=3.10
|
||||
|
||||
ENV QL_DIR=/ql \
|
||||
QL_BRANCH=${QL_BRANCH} \
|
||||
LANG=C.UTF-8 \
|
||||
SHELL=/bin/bash \
|
||||
PS1="\u@\h:\w \$ " \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
VOLUME /ql/data
|
||||
|
||||
EXPOSE 5700
|
||||
|
||||
COPY --from=builder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/
|
||||
COPY --from=builder /usr/local/bin/. /usr/local/bin/
|
||||
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
coreutils \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
tzdata \
|
||||
perl \
|
||||
openssl \
|
||||
nodejs \
|
||||
jq \
|
||||
openssh-client \
|
||||
procps \
|
||||
netcat-openbsd \
|
||||
unzip \
|
||||
npm \
|
||||
cron \
|
||||
ca-certificates \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& echo "Asia/Shanghai" > /etc/timezone \
|
||||
&& git config --global user.email "qinglong@users.noreply.github.com" \
|
||||
&& git config --global user.name "qinglong" \
|
||||
&& git config --global http.postBuffer 524288000 \
|
||||
&& rm -rf /root/.cache
|
||||
|
||||
ARG SOURCE_COMMIT
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh \
|
||||
&& git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
|
||||
&& mkdir -p ${QL_DIR}/static \
|
||||
&& cp -rf /static/* ${QL_DIR}/static \
|
||||
&& rm -rf /static
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
|
||||
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
|
||||
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
|
||||
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
|
||||
|
||||
RUN pip3 install --prefix ${PYTHON_HOME} requests
|
||||
|
||||
COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/
|
||||
|
||||
WORKDIR ${QL_DIR}
|
||||
|
||||
HEALTHCHECK --interval=5s --timeout=2s --retries=20 \
|
||||
CMD curl -sf --noproxy '*' http://127.0.0.1:5700/api/health || exit 1
|
||||
|
||||
ENTRYPOINT ["./docker/docker-entrypoint.sh"]
|
||||
@@ -1,93 +0,0 @@
|
||||
FROM python:3.11-slim-bookworm AS builder
|
||||
COPY package.json .npmrc pnpm-lock.yaml /tmp/build/
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends nodejs npm git ca-certificates \
|
||||
&& npm i -g pnpm@8.3.1 pm2 ts-node \
|
||||
&& cd /tmp/build \
|
||||
&& pnpm install --prod \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
FROM python:3.11-slim-bookworm
|
||||
|
||||
ARG QL_MAINTAINER="whyour"
|
||||
LABEL maintainer="${QL_MAINTAINER}"
|
||||
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
|
||||
ARG QL_BRANCH=develop
|
||||
ARG PYTHON_SHORT_VERSION=3.11
|
||||
|
||||
ENV QL_DIR=/ql \
|
||||
QL_BRANCH=${QL_BRANCH} \
|
||||
LANG=C.UTF-8 \
|
||||
SHELL=/bin/bash \
|
||||
PS1="\u@\h:\w \$ " \
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
VOLUME /ql/data
|
||||
|
||||
EXPOSE 5700
|
||||
|
||||
COPY --from=builder /usr/local/lib/node_modules/. /usr/local/lib/node_modules/
|
||||
COPY --from=builder /usr/local/bin/. /usr/local/bin/
|
||||
|
||||
RUN set -x \
|
||||
&& apt-get update \
|
||||
&& apt-get upgrade -y \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
bash \
|
||||
coreutils \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
tzdata \
|
||||
perl \
|
||||
openssl \
|
||||
nodejs \
|
||||
jq \
|
||||
openssh-client \
|
||||
procps \
|
||||
netcat-openbsd \
|
||||
unzip \
|
||||
npm \
|
||||
cron \
|
||||
ca-certificates \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
|
||||
&& echo "Asia/Shanghai" > /etc/timezone \
|
||||
&& git config --global user.email "qinglong@users.noreply.github.com" \
|
||||
&& git config --global user.name "qinglong" \
|
||||
&& git config --global http.postBuffer 524288000 \
|
||||
&& rm -rf /root/.cache
|
||||
|
||||
ARG SOURCE_COMMIT
|
||||
RUN git clone --depth=1 -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
|
||||
&& cd ${QL_DIR} \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh \
|
||||
&& git clone --depth=1 -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
|
||||
&& mkdir -p ${QL_DIR}/static \
|
||||
&& cp -rf /static/* ${QL_DIR}/static \
|
||||
&& rm -rf /static
|
||||
|
||||
ENV PNPM_HOME=${QL_DIR}/data/dep_cache/node \
|
||||
PYTHON_HOME=${QL_DIR}/data/dep_cache/python3 \
|
||||
PYTHONUSERBASE=${QL_DIR}/data/dep_cache/python3
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PNPM_HOME}:${PYTHON_HOME}/bin \
|
||||
NODE_PATH=/usr/local/bin:/usr/local/lib/node_modules:${PNPM_HOME}/global/5/node_modules \
|
||||
PIP_CACHE_DIR=${PYTHON_HOME}/pip \
|
||||
PYTHONPATH=${PYTHON_HOME}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}:${PYTHON_HOME}/lib/python${PYTHON_SHORT_VERSION}/site-packages
|
||||
|
||||
RUN pip3 install --prefix ${PYTHON_HOME} requests
|
||||
|
||||
COPY --from=builder /tmp/build/node_modules/. /ql/node_modules/
|
||||
|
||||
WORKDIR ${QL_DIR}
|
||||
|
||||
HEALTHCHECK --interval=5s --timeout=2s --retries=20 \
|
||||
CMD curl -sf --noproxy '*' http://127.0.0.1:5700/api/health || exit 1
|
||||
|
||||
ENTRYPOINT ["./docker/docker-entrypoint.sh"]
|
||||
@@ -0,0 +1,352 @@
|
||||
# Qinglong Python API (QLAPI) Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The Qinglong Python API provides a convenient way to interact with the Qinglong system from Python scripts. The `QLAPI` object is automatically available in your Python scripts when they run within the Qinglong environment.
|
||||
|
||||
## Availability
|
||||
|
||||
The Python QLAPI is available starting from **version 2.8.0+**. If you're using an older version (e.g., v2.7.11), please upgrade to access these features.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Qinglong version 2.8.0 or higher
|
||||
- Python 3.6 or higher
|
||||
- Running within Qinglong environment (scripts executed through Qinglong task system)
|
||||
|
||||
## Usage
|
||||
|
||||
The `QLAPI` object is automatically injected into your Python script's global namespace. You don't need to import anything - just use it directly:
|
||||
|
||||
```python
|
||||
# QLAPI is automatically available
|
||||
result = QLAPI.getEnvs({"searchValue": "USER"})
|
||||
print(result)
|
||||
```
|
||||
|
||||
## Available Methods
|
||||
|
||||
### Environment Variables Management
|
||||
|
||||
#### getEnvs
|
||||
Get environment variables with optional search filter.
|
||||
|
||||
```python
|
||||
# Get all environment variables
|
||||
envs = QLAPI.getEnvs()
|
||||
|
||||
# Search for specific environment variables
|
||||
envs = QLAPI.getEnvs({"searchValue": "USER"})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `searchValue` (optional): String to search for in environment variable names or values
|
||||
|
||||
**Returns:** `EnvsResponse` with list of environment variables
|
||||
|
||||
#### createEnv
|
||||
Create new environment variables.
|
||||
|
||||
```python
|
||||
result = QLAPI.createEnv({
|
||||
"envs": [
|
||||
{
|
||||
"name": "MY_VAR",
|
||||
"value": "my_value",
|
||||
"remarks": "My custom variable"
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `envs`: List of environment variable objects to create
|
||||
|
||||
**Returns:** `EnvsResponse`
|
||||
|
||||
#### updateEnv
|
||||
Update an existing environment variable.
|
||||
|
||||
```python
|
||||
result = QLAPI.updateEnv({
|
||||
"env": {
|
||||
"id": 123,
|
||||
"name": "MY_VAR",
|
||||
"value": "new_value",
|
||||
"remarks": "Updated variable"
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `env`: Environment variable object with id and updated fields
|
||||
|
||||
**Returns:** `EnvResponse`
|
||||
|
||||
#### deleteEnvs
|
||||
Delete environment variables by IDs.
|
||||
|
||||
```python
|
||||
result = QLAPI.deleteEnvs({"ids": [123, 456]})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids`: List of environment variable IDs to delete
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
#### enableEnvs
|
||||
Enable environment variables.
|
||||
|
||||
```python
|
||||
result = QLAPI.enableEnvs({"ids": [123, 456]})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids`: List of environment variable IDs to enable
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
#### disableEnvs
|
||||
Disable environment variables.
|
||||
|
||||
```python
|
||||
result = QLAPI.disableEnvs({"ids": [123, 456]})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids`: List of environment variable IDs to disable
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
#### updateEnvNames
|
||||
Update names of multiple environment variables.
|
||||
|
||||
```python
|
||||
result = QLAPI.updateEnvNames({
|
||||
"ids": [123, 456],
|
||||
"name": "NEW_NAME"
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids`: List of environment variable IDs
|
||||
- `name`: New name to set
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
#### getEnvById
|
||||
Get a specific environment variable by ID.
|
||||
|
||||
```python
|
||||
env = QLAPI.getEnvById({"id": 123})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `id`: Environment variable ID
|
||||
|
||||
**Returns:** `EnvResponse`
|
||||
|
||||
#### moveEnv
|
||||
Change the position/order of an environment variable.
|
||||
|
||||
```python
|
||||
result = QLAPI.moveEnv({
|
||||
"id": 123,
|
||||
"fromIndex": 0,
|
||||
"toIndex": 5
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `id`: Environment variable ID
|
||||
- `fromIndex`: Current position index
|
||||
- `toIndex`: Target position index
|
||||
|
||||
**Returns:** `EnvResponse`
|
||||
|
||||
### Scheduled Tasks (Cron) Management
|
||||
|
||||
#### getCronDetail
|
||||
Get details of a scheduled task.
|
||||
|
||||
```python
|
||||
cron = QLAPI.getCronDetail({"log_path": "/path/to/log"})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `log_path`: Path to the task log file
|
||||
|
||||
**Returns:** `CronResponse`
|
||||
|
||||
#### createCron
|
||||
Create a new scheduled task.
|
||||
|
||||
```python
|
||||
result = QLAPI.createCron({
|
||||
"command": "node script.js",
|
||||
"schedule": "0 0 * * *",
|
||||
"name": "Daily Task",
|
||||
"labels": ["tag1", "tag2"],
|
||||
"sub_id": None,
|
||||
"extra_schedules": [],
|
||||
"task_before": "",
|
||||
"task_after": ""
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `command`: Command to execute
|
||||
- `schedule`: Cron expression
|
||||
- `name`: Task name (optional)
|
||||
- `labels`: List of labels (optional)
|
||||
- Other optional fields
|
||||
|
||||
**Returns:** `CronResponse`
|
||||
|
||||
#### updateCron
|
||||
Update an existing scheduled task.
|
||||
|
||||
```python
|
||||
result = QLAPI.updateCron({
|
||||
"id": 123,
|
||||
"command": "node updated_script.js",
|
||||
"schedule": "0 0 * * *",
|
||||
"name": "Updated Task",
|
||||
"labels": [],
|
||||
"sub_id": None,
|
||||
"extra_schedules": [],
|
||||
"task_before": "",
|
||||
"task_after": ""
|
||||
})
|
||||
```
|
||||
|
||||
**Returns:** `CronResponse`
|
||||
|
||||
#### deleteCrons
|
||||
Delete scheduled tasks by IDs.
|
||||
|
||||
```python
|
||||
result = QLAPI.deleteCrons({"ids": [123, 456]})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids`: List of task IDs to delete
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
### Notifications
|
||||
|
||||
#### notify
|
||||
Send a notification using configured notification channels.
|
||||
|
||||
```python
|
||||
result = QLAPI.notify("Notification Title", "Notification Content")
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- First argument: Notification title
|
||||
- Second argument: Notification content
|
||||
|
||||
**Returns:** Notification result
|
||||
|
||||
#### systemNotify
|
||||
Send a system notification with custom parameters.
|
||||
|
||||
```python
|
||||
result = QLAPI.systemNotify({
|
||||
"title": "System Alert",
|
||||
"content": "This is a system notification"
|
||||
})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `title`: Notification title
|
||||
- `content`: Notification content
|
||||
|
||||
**Returns:** `Response`
|
||||
|
||||
## Complete Example
|
||||
|
||||
```python
|
||||
"""
|
||||
Example Qinglong Python script demonstrating QLAPI usage
|
||||
"""
|
||||
|
||||
# Get environment variables
|
||||
print("Fetching environment variables...")
|
||||
envs = QLAPI.getEnvs({"searchValue": "TOKEN"})
|
||||
print(f"Found {len(envs.get('data', []))} environment variables")
|
||||
|
||||
# Create a new environment variable
|
||||
print("Creating new environment variable...")
|
||||
result = QLAPI.createEnv({
|
||||
"envs": [
|
||||
{
|
||||
"name": "MY_TEST_VAR",
|
||||
"value": "test_value_123",
|
||||
"remarks": "Created by script"
|
||||
}
|
||||
]
|
||||
})
|
||||
print(f"Create result: {result}")
|
||||
|
||||
# Send notification
|
||||
print("Sending notification...")
|
||||
QLAPI.notify("Script Completed", "The script has finished executing successfully")
|
||||
|
||||
# Send system notification
|
||||
QLAPI.systemNotify({
|
||||
"title": "Task Report",
|
||||
"content": f"Processed {len(envs.get('data', []))} environment variables"
|
||||
})
|
||||
|
||||
print("Done!")
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
All QLAPI methods may raise exceptions if there are errors communicating with the backend. It's recommended to use try-except blocks:
|
||||
|
||||
```python
|
||||
try:
|
||||
envs = QLAPI.getEnvs({"searchValue": "USER"})
|
||||
print(f"Success: {envs}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
QLAPI.notify("Script Error", str(e))
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### AttributeError: 'BaseApi' object has no attribute 'getEnvs'
|
||||
|
||||
This error occurs when using an older version of Qinglong (before v2.8.0). Solutions:
|
||||
|
||||
1. **Upgrade Qinglong**: Update to version 2.8.0 or higher
|
||||
2. **Check Installation**: Ensure `shell/preload/client.py` exists
|
||||
3. **Verify Environment**: Make sure scripts are running within Qinglong environment
|
||||
|
||||
### Module Import Errors
|
||||
|
||||
The QLAPI is only available when scripts run within the Qinglong environment. If you're testing locally, you won't have access to QLAPI.
|
||||
|
||||
## Technical Details
|
||||
|
||||
The Python QLAPI is a wrapper around the Node.js gRPC API client. When you call a method like `QLAPI.getEnvs()`:
|
||||
|
||||
1. Python Client creates a temporary Node.js script
|
||||
2. Executes the corresponding Node.js API method
|
||||
3. Returns the JSON result back to Python
|
||||
|
||||
This architecture ensures consistency between JavaScript and Python APIs.
|
||||
|
||||
## See Also
|
||||
|
||||
For more information and examples:
|
||||
|
||||
- [JavaScript API Client Source](./client.js) - The underlying Node.js gRPC client
|
||||
- [Python Sample Script](../../sample/ql_sample.py) - Example Python script using QLAPI
|
||||
- [JavaScript Sample Script](../../sample/ql_sample.js) - Example JavaScript script for comparison
|
||||
|
||||
These files are located in the Qinglong repository under `shell/preload/` and `sample/` directories.
|
||||
@@ -1,3 +1,20 @@
|
||||
"""
|
||||
Qinglong Python API Client
|
||||
|
||||
This module provides a Python interface to the Qinglong API through a gRPC-based
|
||||
Node.js client. It enables Python scripts to interact with Qinglong's environment
|
||||
variables, scheduled tasks, and notification systems.
|
||||
|
||||
The Client class is used as a base for the QLAPI object that is automatically
|
||||
available in Qinglong Python scripts. Users can call methods like:
|
||||
|
||||
QLAPI.getEnvs({"searchValue": "USER"})
|
||||
QLAPI.createEnv({"envs": [{"name": "VAR", "value": "val"}]})
|
||||
QLAPI.notify("Title", "Content")
|
||||
|
||||
For detailed documentation, see README_PYTHON_API.md
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import json
|
||||
import tempfile
|
||||
@@ -176,6 +193,23 @@ class CronResponse(TypedDict):
|
||||
|
||||
|
||||
class Client:
|
||||
"""
|
||||
Qinglong API Client for Python.
|
||||
|
||||
This class provides methods to interact with Qinglong's API for managing
|
||||
environment variables, scheduled tasks (crons), and notifications.
|
||||
|
||||
The client works by executing Node.js code that calls the actual gRPC API,
|
||||
then returning the results to Python. This ensures consistency between
|
||||
JavaScript and Python API interfaces.
|
||||
|
||||
Usage:
|
||||
client = Client()
|
||||
envs = client.getEnvs({"searchValue": "TOKEN"})
|
||||
|
||||
Note: This class is typically used through the QLAPI object which is
|
||||
automatically available in Qinglong Python scripts.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.temp_dir = tempfile.mkdtemp(prefix="node_client_")
|
||||
self.temp_script = os.path.join(self.temp_dir, "temp_script.js")
|
||||
|
||||
@@ -131,10 +131,14 @@ try:
|
||||
|
||||
from __ql_notify__ import send
|
||||
|
||||
# BaseApi inherits all methods from Client (getEnvs, createEnv, etc.)
|
||||
# and adds the notify method for sending notifications
|
||||
class BaseApi(Client):
|
||||
def notify(self, *args, **kwargs):
|
||||
return send(*args, **kwargs)
|
||||
|
||||
# Create QLAPI instance and make it globally available
|
||||
# This allows scripts to use: QLAPI.getEnvs(), QLAPI.notify(), etc.
|
||||
QLAPI = BaseApi()
|
||||
builtins.QLAPI = QLAPI
|
||||
except Exception as error:
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Validation script for QLAPI Client functionality.
|
||||
Tests that all methods are properly accessible through the BaseApi class.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from client import Client
|
||||
|
||||
|
||||
def test_client_has_required_methods():
|
||||
"""Test that Client class has all required API methods."""
|
||||
client = Client()
|
||||
|
||||
required_methods = [
|
||||
'getEnvs',
|
||||
'createEnv',
|
||||
'updateEnv',
|
||||
'deleteEnvs',
|
||||
'moveEnv',
|
||||
'disableEnvs',
|
||||
'enableEnvs',
|
||||
'updateEnvNames',
|
||||
'getEnvById',
|
||||
'systemNotify',
|
||||
'getCronDetail',
|
||||
'createCron',
|
||||
'updateCron',
|
||||
'deleteCrons',
|
||||
]
|
||||
|
||||
print("Testing Client class methods...")
|
||||
for method in required_methods:
|
||||
assert hasattr(client, method), f"Client missing method: {method}"
|
||||
assert callable(getattr(client, method)), f"Client.{method} is not callable"
|
||||
print(f" ✓ {method}")
|
||||
|
||||
print(f"\n✓ All {len(required_methods)} methods are present and callable")
|
||||
return True
|
||||
|
||||
|
||||
def test_baseapi_inheritance():
|
||||
"""Test that BaseApi properly inherits from Client."""
|
||||
|
||||
# Simulate the BaseApi class from sitecustomize.py
|
||||
class BaseApi(Client):
|
||||
def notify(self, *args, **kwargs):
|
||||
return "mock_notify_result"
|
||||
|
||||
api = BaseApi()
|
||||
|
||||
print("\nTesting BaseApi inheritance...")
|
||||
|
||||
# Test that BaseApi has all Client methods
|
||||
assert hasattr(api, 'getEnvs'), "BaseApi missing getEnvs"
|
||||
assert callable(api.getEnvs), "BaseApi.getEnvs is not callable"
|
||||
print(" ✓ getEnvs is accessible")
|
||||
|
||||
# Test that BaseApi also has its own method
|
||||
assert hasattr(api, 'notify'), "BaseApi missing notify"
|
||||
assert callable(api.notify), "BaseApi.notify is not callable"
|
||||
print(" ✓ notify is accessible")
|
||||
|
||||
# Verify getEnvs has type annotations (either params or return)
|
||||
annotations = api.getEnvs.__annotations__
|
||||
assert len(annotations) > 0, "getEnvs should have type annotations"
|
||||
assert 'return' in annotations, "getEnvs should have return type annotation"
|
||||
print(" ✓ getEnvs has correct signature with type annotations")
|
||||
|
||||
print("\n✓ BaseApi properly inherits from Client and adds notify method")
|
||||
return True
|
||||
|
||||
|
||||
def test_method_signatures():
|
||||
"""Test that methods have correct type annotations."""
|
||||
client = Client()
|
||||
|
||||
print("\nTesting method signatures...")
|
||||
|
||||
# Test getEnvs signature
|
||||
getEnvs_annotations = client.getEnvs.__annotations__
|
||||
# Check that annotations exist - could be 'params', or just 'return'
|
||||
assert len(getEnvs_annotations) > 0, "getEnvs should have type annotations"
|
||||
assert 'return' in getEnvs_annotations, "getEnvs should have return type annotation"
|
||||
print(" ✓ getEnvs has type annotations")
|
||||
|
||||
# Test other critical methods
|
||||
assert hasattr(client, 'createEnv'), "Missing createEnv"
|
||||
assert hasattr(client, 'updateEnv'), "Missing updateEnv"
|
||||
print(" ✓ Critical methods present")
|
||||
|
||||
print("\n✓ All method signatures are correct")
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
"""Run all validation tests."""
|
||||
print("=" * 60)
|
||||
print("QLAPI Client Validation Tests")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
test_client_has_required_methods()
|
||||
test_baseapi_inheritance()
|
||||
test_method_signatures()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("ALL TESTS PASSED ✓")
|
||||
print("=" * 60)
|
||||
print("\nThe QLAPI Client is working correctly.")
|
||||
print("Users can safely use: QLAPI.getEnvs({'searchValue': 'USER'})")
|
||||
return 0
|
||||
|
||||
except AssertionError as e:
|
||||
print(f"\n❌ TEST FAILED: {e}")
|
||||
return 1
|
||||
except Exception as e:
|
||||
print(f"\n❌ UNEXPECTED ERROR: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user