1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/fagongzi-gateway

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
restful.md 35 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 13.03.2025 10:28 9c7636b

RESTful

Кроме GRPC API, сервер API поддерживает RESTful HTTP API. Ниже приведена информация о RESTful API.

Внимание:

  • Поскольку единица времени в Go — наносекунда (time.Duration), время в API должно быть указано в наносекундах. Одна секунда равна 1,000,000,000 наносекундам.
  • В поле body параметра defaultValue данные должны быть закодированы в base64, иначе возникнет ошибка base64.CorruptInputError=недопустимые данные base64. Это связано с типом данных type HTTPResult struct {Body []byte}, для получения более подробной информации посетите [[]byte encodes as a base64-encoded].
  • При конфигурировании renderTemplate, если flatAttrs равно true, имя можно опустить. Если flatAttrs равно false, имя обязательно должно быть указано и не может быть пустым. Это требует проверки данных при вызове API, а неверная конфигурация может привести к недоступности сервиса.
  • Значение defaultValue в узлах должно совпадать со значением renderTemplate, иначе будет выдана ошибка Key path not found.

ENUM

Статус

Имя Значение Комментарий
Down 0
Up 1

Состояние Цепи

Имя Значение Комментарий
Открыто 0
Полуоткрыто 1
Закрыто 2

Балансировка Загрузки

Имя Значение Комментарий
RoundRobin 0
IPHash 1 Текущая версия не поддерживает эту функцию
Имя Значение Комментарий
------------- :-------------: -------------
HTTP 0
GRPC 1 Текущая версия не поддерживается
Dubbo 1 Текущая версия не поддерживается
SpringCloud 2 Текущая версия не поддерживается
Имя Значение Комментарий
------------- :-------------: -------------
QueryString 0
FormData 1
JSONBody 2
Header 3
Cookie 4
PathValue 5

Тип Правила

Имя Значение Комментарий
RuleRegexp 0

Операция Сравнения

Имя Значение Комментарий
CMPEQ 0
CMPLT 1
CMPLE 2
CMPGT 3
CMPGE 4
CMPIn 5
CMPMatch 6

Стратегия маршрутизации

Имя Значение Комментарий
Copy 0
Split 1

Кластер

Новый/Обновленный

URL Метод
/v1/clusters PUT

JSON Body

{
    "id":1,
    "name":"имя кластера",
    "loadBalance":0
}

Значение 1 в поле id указывает на обновление. Ответ

{
    "code":0,
    "data":1
}

Поле data представляет собой идентификатор кластера

Удаление

URL Метод
/v1/clusters/{id} DELETE

Ответ

{
    "code":0,
    "data":"null"
}

Получение данных

URL Метод
/v1/clusters/{id} GET

Ответ

{
    "code":0,
    "data":{
        "id":1,
        "name":"имя кластера",
        "loadBalance":0
    }
}

Поле data представляет собой данные кластера

Получение списка кластеров

URL Метод
/v1/clusters?after=xx&limit=xx GET

after: идентификатор последнего созданного кластера
limit: количество требуемых идентификаторов```json { "code":0, "data":[ { "id":1, "name":"имя кластера", "loadBalance":0 }, { "id":2, "name":"имя кластера", "loadBalance":0 }, { "id":3, "name":"имя кластера", "loadBalance":0 } ] }

Поле `data` представляет собой коллекцию кластеров.
Следующий пакет: `/v1/clusters?after=3&limit=3`
```### Получение всех привязанных серверов
|URL|Метод|
| -------------|:-------------:|
|/v1/clusters/{id}/binds|GET|

Ответ
```json
{
    "code":0,
    "data":[
        1,
        2,
        3
    ]
}

Поле data представляет собой идентификаторы всех привязанных к кластеру серверов.

Отвязка всех серверов

URL Метод
/v1/clusters/{id}/binds DELETE

Ответ

{
    "code":0,
    "data":"null"
}

Сервер

Создание/Обновление

URL Метод
/v1/servers PUT

JSON Body

{
    "id":1,
    "addr":"127.0.0.1:8080",
    "protocol":0,
    "maxQPS":100,
    "healthCheck":{
        "path":"/check-health",
        "body":"OK",
        "checkInterval":10000000000,
        "timeout":30000000000
    },
    "circuitBreaker":{
        "closeTimeout":10000000000,
        "halfTrafficRate":50,
        "rateCheckPeriod":10000000000,
        "failureRateToClose":20,
        "successRateToOpen":30
    }
}

Значение 1 в поле id указывает на обновление.

Ответ

{
    "code":0,
    "data":1
}

Поле data представляет собой идентификатор сервера.

Удаление

URL Метод
/v1/servers/{id} DELETE

Ответ

{
    "code":0,
    "data":"null"
}

Получение данных

URL Метод
/v1/servers/{id} GET

Ответ

{
    "code": 0,
    "data": {
        "id": 1,
        "addr": "127.0.0.1:8080",
        "protocol": 0,
        "maxQPS": 100,
        "healthCheck": {
            "path": "/check-health",
            "body": "OK",
            "checkInterval": 10000000000,
            "timeout": 30000000000
        },
        "circuitBreaker": {
            "closeTimeout": 10000000000,
            "halfTrafficRate": 50,
            "rateCheckPeriod": 10000000000,
            "failureRateToClose": 20,
            "successRateToOpen": 30
        }
    }
}

Поле data отражает информацию о сервере.### Список

URL Метод
/v1/servers?after=xx&limit=xx GET

after: последний идентификатор сервера
limit: количество серверов, информацию о которых требуется получить

Ответ

{
    "code": 0,
    "data": [
        {
            "id": 1,
            "addr": "127.0.0.1:8081",
            "protocol": 0,
            "maxQPS": 100,
            "healthCheck": {
                "path": "/check-health",
                "body": "OK",
                "checkInterval": 10000000000,
                "timeout": 30000000000
            },
            "circuitBreaker": {
                "closeTimeout": 10000000000,
                "halfTrafficRate": 50,
                "rateCheckPeriod": 10000000000,
                "failureRateToClose": 20,
                "successRateToOpen": 30
            }
        },
        {
            "id": 2,
            "addr": "127.0.0.1:8082",
            "protocol": 0,
            "maxQPS": 100,
            "healthCheck": {
                "path": "/check-health",
                "body": "OK",
                "checkInterval": 10000000000,
                "timeout": 30000000000
            },
            "circuitBreaker": {
                "closeTimeout": 10000000000,
                "halfTrafficRate": 50,
                "rateCheckPeriod": 10000000000,
                "failureRateToClose": 20,
                "successRateToOpen": 30
            }
        },
        {
            "id": 3,
            "addr": "127.0.0.1:8083",
            "protocol": 0,
            "maxQPS": 100,
            "healthCheck": {
                "path": "/check-health",
                "body": "OK",
                "checkInterval": 10000000000,
                "timeout": 30000000000
            },
            "circuitBreaker": {
                "closeTimeout": 10000000000,
                "halfTrafficRate": 50,
                "rateCheckPeriod": 10000000000,
                "failureRateToClose": 20,
                "successRateToOpen": 30
            }
        }
    ]
}

Поля данных содержат коллекцию серверов. Следующий пакет: /v1/servers?after=3&limit=3

Bind

Добавление

URL Метод
/v1/binds PUT
{
    "clusterID":1,
    "serverID":2
}

Ответ

{
    "code":0,
    "data":"null"
}

Удаление

URL Метод
/v1/binds DELETE

JSON Тело

{
    "clusterID":1,
    "serverID":2
}

Ответ

{
    "code":0,
    "data":"null"
}
```## API
### Новый/Обновленный
|URL|Метод|
| -------------|:-------------:|
|/v1/apis|PUT|JSON Тело
```json
{
    "id":1,
    "name":"demo-api",
    "urlPattern":"^/api/users/(\\d+)$",
    "method":"GET",
    "domain":"www.xxx.com",
    "status":1,
    "ipAccessControl":{
        "whitelist":[
            "127.*",
            "192.168.*",
            "172.17.*",
            "172.17.1.1"
        ],
        "blacklist":[
            "127.*",
            "192.168.*",
            "172.17.*",
            "172.17.1.1"
        ]
    },
    "defaultValue":{
        "code":200,
        "body":"aGVsbG8gd29ybGQ=",
        "headers":[
            {
                "name":"xx",
                "value":"xx"
            }
        ],
        "cookies":[
            {
                "name":"xx",
                "value":"xx"
            }
        ]
    },
    "nodes":[
        {
            "clusterID":1,
            "urlRewrite":"/users?id=$1",
            "attrName":"user",
            "validations":[
                {
                    "parameter":{
                        "name":"id",
                        "source":0,
                        "index":0
                    },
                    "required":true,
                    "rules":[
                        {
                            "ruleType":0,
                            "expression":"^\\d+$"
                        }
                    ]
                }
            ],
            "cache":{
                "keys":[
                    {
                        "name":"id",
                        "source":0,
                        "index":0
                    }
                ],
                "deadline":100,
                "conditions":[
                    {
                        "parameter":{
                            "name":"id",
                            "source":0,
                            "index":0
                        },
                        "cmp":3,
                        "expect":"100"
                    }
                ]
            }
        },
        {
            "clusterID":2,
            "urlRewrite":"/users/$1/account",
            "attrName":"account",
            "validations":[
                {
                    "parameter":{
                        "name":"",
                        "source":5,
                        "index":0
                    },
                    "required":false,
                    "rules":[]
                }
            ],
            "cache":{
                "keys":[],
                "deadline":0,
                "conditions":[]
            }
        }
    ]
}
``````markdown
{
    "ruleType": 0,
    "expression": "^\\d+$"
}
],
"default_value": {
    "code": 200,
    "body": "eyJjb2RlIjoxLCAiZGF0YSI6eyJtZXNzYWdlIjogIuacjeWKoeS4jeWPr+eU qOi/meaYr+afkOiKgueCueeahOm7mOiupOWAvO+8gSJ9fQ==",
    "headers": [
        {
            "name": "Content-Type",
            "value": "application/json"
        }
    ]
},
"auth_filter": "CUSTOM_JWT",
"render_template": {
    "objects": [
        {
            "name": "",
            "attrs": [
                {
                    "name": "user",
                    "extract_exp": "user.data"
                },
                {
                    "name": "account",
                    "extract_exp": "account.data"
                }
            ],
            "flat_attrs": true
        }
    ]
},
"use_default": false,
"match_rule": 0,
"position": 0,
"tags": [
    {
        "name": "tag3",
        "value": "value3"
    }
]
}
```1 в поле `id` означает обновление.
```yaml
Ответ
```json
{
    "code": 0,
    "data": 1
}

Поле data представляет собой идентификатор API.

Удаление

URL Метод
/v1/apis/{id} DELETE

Ответ

{
    "code": 0,
    "data": "null"
}

Получение данных

URL Метод
/v1/apis/{id} GET
Ответ```json
{
    "code": 0,
    "data": {
        "id": 1,
        "name": "demo-api",
        "urlPattern": "^/api/users/(\\d+)$",
        "method": "GET",
        "domain": "www.xxx.com",
        "status": 1,
        "ipAccessControl": {
            "whitelist": [
                "127.*",
                "192.168.*",
                "172.17.*",
                "172.17.1.1"
            ],
            "blacklist": [
                "127.*",
                "192.168.*",
                "172.17.*",
                "172.17.1.1"
            ]
        },
        "defaultValue": {
            "code": 200,
            "body": "aGVsbG8gd29ybGQ=",
            "headers": [
                {
                    "name": "xx",
                    "value": "xx"
                }
            ],
            "cookies": [
                {
                    "name": "xx",
                    "value": "xx"
                }
            ]
        },
        "nodes": [
            {
                "clusterID": 1,
                "urlRewrite": "/users?id=$1",
                "attrName": "user",
                "validations": [
                    {
                        "parameter": {
                            "name": "id",
                            "source": 0,
                            "index": 0
                        },
                        "required": true,
                        "rules": [
                            {
                                "ruleType": 0,
                                "expression": "^\\d+$"
                            }
                        ]
                    }
                ],
                "cache": {
                    "keys": [
                        {
                            "name": "id",
                            "source": 0,
                            "index": 0
                        }
                    ],
                    "deadline": 100,
                    "conditions": [
                        {
                            "parameter": {
                                "name": "id",
                                "source": 0,
                                "index": 0
                            },
                            "cmp": 3,
                            "expect": "100"
                        }
                    ]
                }
            },
            {
                "clusterID": 2,
            }
        ]
    }
}
``````markdown
            "urlRewrite": "/пользователи/$1/аккаунт",
             "attrName": "аккаунт",
             "validations": [
                 {
                     "parameter": {
                         "name": "",
                         "source": 5,
                         "index": 1
                     },
                     "required": true,
                     "rules": [
                         {
                             "ruleType": 0,
                             "expression": "^\\d+$"
                         }
                     ]
                 }
             ]
         },
         "authFilter": "CUSTOM_JWT",
         "renderTemplate": {
             "objects": [
                 {
                     "name": "",
                     "attrs": [
                         {
                             "name": "пользователь",
                             "extractExp": "user.data"
                         },
                         {
                             "name": "аккаунт",
                             "extractExp": "account.data"
                         }
                     ],
                     "flatAttrs": true
                 }
             ]
         },
         "useDefault": false,
         "matchRule": 0,
         "position": 0,
         "tags": [
             {
                 "name": "tag3",
                 "value": "value3"
             }
         ]
     }
 }

Поле данных представляет собой идентификатор API.

Список запросов

URL Метод
/v1/apis?after=xx&limit=xx GET
{
    "code": 0,
    "data": [
        {
            "id": 1,
            "name": "demo-api",
            "template_path": "^/api/users/(\\d+)$",
            "method": "GET",
            "domain": "www.xxx.com",
            "status": 1,
            "access_control_ip": {
                "white_list": [
                    "127.*",
                    "192.168.*",
                    "172.17.*",
                    "172.17.1.1"
                ],
                "black_list": [
                    "127.*",
                    "192.168.*",
                    "172.17.*",
                    "172.17.1.1"
                ]
            },
            "default_value": {
                "code": 200,
                "body": "aGVsbG8gd29ybGQ=",
                "headers": [
                    {
                        "name": "xx",
                        "value": "xx"
                    }
                ],
                "cookies": [
                    {
                        "name": "xx",
                        "value": "xx"
                    }
                ]
            },
            "nodes": [
                {
                    "cluster_id": 1,
                    "path_rewrite": "/users?id=$1",
                    "attribute_name": "user",
                    "validation": [
                        {
                            "parameter": {
                                "name": "id",
                                "source": 0,
                                "index": 0
                            },
                            "required": true,
                            "rules": [
                                {
                                    "rule_type": 0,
                                    "expression": "^\\d+$"
                                }
                            ]
                        }
                    ],
                    "caching": {

Пожалуйста, обратите внимание, что часть текста была переведена, но некоторые ключевые слова и значения остались без изменений, чтобы сохранить структуру и смысл оригинального документа.```markdown "ключи":[ { "название":"id", "источник":0, "индекс":0 } ], "время_жизненности":100, "условия":[ { "параметр":{ "название":"id", "источник":0, "индекс":0 }, "cmp":3, "ожидаемое":"100" } ] }, { "clusterID":2, "urlRewrite":"/пользователи/$1/аккаунт", "attrName":"аккаунт", "валидации":[ { "параметр":{ "название":"", "источник":5, "индекс":1 }, "необходимость":true, "правила":[ { "тип_правила":0, "выражение":"^\d+$" } ] } ] } ], "фильтр_авторизации":"CUSTOM_JWT", "рендерить_шаблон":{ "объекты":[ { "название":"", "атрибуты":[ { "название":"пользователь", "выделить_по_выражению":"user.data" }, { "название":"аккаунт",

```markdown
                                 "выделить_по_выражению": "account.data"
                              },
                          ],
                          "плоские_атрибуты": true
                      }
                  ],
              },
              "использовать_по_умолчанию": false,
              "правило_соответствия": 0,
              "позиция": 0,
              "тэги": [
                  {
                      "название": "tag1",
                      "значение": "value1"
                  }
              ]
          },
          {
              "id": 2,
              "название": "demo-api-2",
              "urlPattern": "^/api/пользователи/(\\d+)$",
              "метод": "GET",
              "домен": "www.xxx.com",
              "статус": 1,
              "ipAccessControl": {
                  "белыйСписок": [
                      "127.*",
                      "192.168.*",
                      "172.17.*",
                      "172.17.1.1"
                  ],
                  "черныйСписок": [
                      "127.*",
                      "192.168.*",
                      "172.17.*",
                      "172.17.1.1"
                  ]
              },
              "defaultValue": {
                  "code": 200,
                  "body": "aGVsbG8gd29ybGQ=",
                  "headers": [
                      {
                          "name": "xx",
                          "value": "xx"
                      }
                  ],
                  "cookies": [
                      {
                          "name": "xx",
                          "value": "xx"
                      }
                  ]
              },
              "nodes": [
                  {
                      "clusterID": 1,
                      "urlRewrite": "/пользователи?id=$1",
                      "attrName": "пользователь",
                      "validations": [
                          {
                              "parameter": {
                                  "name": "id",
                                  "source": 0,
                                  "index": 0
                              },
                              "required": true,
                              "rules": [
                                  {
                                      "type": "expression",
                                      "pattern": "\\d+"
                                  }
                              ]
                          }
                      ]
                  }
              ]
          }
                                     "ruleType":0,
                                      "expression":"^\\d+$"
                                  }
                              ]
                          }
                      ],
                      "cache":{
                          "keys":[
                              {
                                  "name":"id",
                                  "source":0,
                                  "index":0
                              }
                          ],
                          "deadline":100,
                          "conditions":[
                              {
                                  "parameter":{
                                      "name":"id",
                                      "source":0,
                                      "index":0
                                  },
                                  "cmp":3,
                                  "expect":"100"
                              }
                          ]
                      }
                  },
                  {
                      "clusterID":2,
                      "urlRewrite":"/пользователи/$1/аккаунт",
                      "attrName":"аккаунт",
                      "validations":[
                          {
                              "parameter":{
```Пожалуйста, предоставьте текст для перевода, чтобы я мог его исправить согласно указанным правилам.```markdown
                              "parameter": {
                                  "name": "",
                                  "source": 5,
                                  "index": 1
                              },
                              "required": true,
                              "rules": [
                                  {
                                      "ruleType": 0,
                                      "expression": "^\\d+$"
                                  }
                              ]
                          }
                      ]
                  },
                  "authFilter": "CUSTOM_JWT",
                  "renderTemplate": {
                      "objects": [
                          {
                              "name": "",
                              "attrs": [
                                  {
                                      "name": "user",
                                      "extractExp": "user.data"
                                  },
                                  {
---
api:
  - id: 2
    name: demo-api-2
    urlPattern: "^/api/accounts/(\\d+)$"
    method: GET
    domain: "www.example.com"
    status: 1
    ipAccessControl:
      whitelist: 
        - "127.*"
        - "192.168.*"
        - "172.17.*"
        - "172.17.1.1"
      blacklist: 
        - "127.*"
        - "192.168.*"
        - "172.17.*"
        - "172.17.1.1"
    defaultValue:
      code: 200
      body: "aGVsbG8gd29ybGQ="
      headers:
        - name: xx
          value: xx
      cookies:
        - name: xx
          value: xx
    nodes:
      - clusterID: 1
        urlRewrite: "/users?id=$1"
        attrName: user
        validations:
          - parameter:
              name: id
              source: 0
              index: 0
            required: true
            rules:
              - ruleType: 0
                expression: "^\\d+$"
        cache:
          keys:
            - name: id
              source: 0
---
api:
  - id: 3
    name: demo-api-3
    urlPattern: "^/api/users/(\\d+)$"
    method: GET
    domain: "www.xxx.com"
    status: 1
    ipAccessControl:
      whitelist: 
        - "127.*"
        - "192.168.*"
        - "172.17.*"
        - "172.17.1.1"
      blacklist: 
        - "127.*"
        - "192.168.*"
        - "172.17.*"
        - "172.17.1.1"
    defaultValue:
      code: 200
      body: "aGVsbG8gd29ybGQ="
      headers:
        - name: xx
          value: xx
      cookies:
        - name: xx
          value: xx
    useDefault: false
    matchRule: 0
    position: 0
    tags:
      - name: tag2
        value: ""
    nodes:
      - clusterID: 1
        urlRewrite: "/users?  id=$1"
        attrName: user
        validations:
          - parameter:
              name: id
              source: 0
              index: 0
            required: true
            rules:
              - ruleType: 0
                expression: "^\\d+$"
        cache:
          keys:
            - name: id
              source: 0
                         "index": 0
                      }
                  ],
                  "deadline": 100,
                  "conditions": [
                      {
                          "parameter": {
                              "name": "id",
                              "source": 0,
                              "index": 0
                          },
                          "cmp": 3,
                          "expect": "100"
                      }
                  ]
              }
          },
          {
              "clusterID": 2,
              "urlRewrite": "/users/$1/account",
              "attrName": "account",
              "validations": [
                  {
                      "parameter": {
                          "name": "",
                          "source": 5,
                          "index": 1
                      },
                      "required": true,
                      "rules": [
                          {
                              "ruleType": 0,
                              "expression": "^\\d+$"
                          }
                      ]
                  }
              ]
          }
      ],
"authFilter": "CUSTOM_JWT",
"renderTemplate": {
    "objects": [
        {
            "name": "",
            "attrs": [
                {
                    "name": "user",
                    "extractExp": "user.data"
                },
                {
                    "name": "account",
                    "extractExp": "account.data"
                }
            ],
            "flatAttrs": true
        }
    ]
},
"useDefault": false,
"matchRule": 0,
"position": 0,
"tags": [
    {
        "name": "tag3",
        "value": "значение3"
    }
]
}

поле данных представляет собой список API Следующий пакет: /v1/apis?после=3&ограничение=3

### Создание/Обновление
|URL|Метод|
| -------------|:-------------:|
|/v1/routings|PUT|

JSON Тело запроса
```json
{
    "id":1,
    "clusterID":2,
    "conditions":[
        {
            "parameter":{
                "name":"id",
                "source":4,
                "index":0
            },
            "cmp":6,
            "expect":"^.+[2]$"
        }
    ],
    "strategy":1,
    "trafficRate":10,
    "status":1,
    "api":1,
    "name":"test-AB"
}

Значение 1 в поле id указывает на обновление.

Ответ

{
    "code":0,
    "data":1
}

Поле data содержит ID шлюза.

Удаление

URL Метод
/v1/routings/{id} DELETE

Ответ

{
    "code":0,
    "data":"null"
}

Получение информации

URL Метод
/v1/routings/{id} GET
```json
{
    "code":0,
    "data":{
        "id":1,
        "clusterID":2,
        "conditions":[
            {
                "parameter":{
                    "name":"id",
                    "source":4,
                    "index":0
                },
                "cmp":6,
                "expect":"^.+[2]$"
            }
        ],
        "strategy":1,
        "trafficRate":10,
        "status":1,
        "api":1,
        "name":"test-AB"
    }
}

Поле data отражает информацию о получаемом шлюзе.### Получение списка

URL Метод
/v1/routings?after=xx&limit=xx GET

after: последний ID шлюза
limit: количество записей

Ответ

{
    "code": 0,
    "data": [
        {
            "id": 1,
            "clusterID": 2,
            "conditions": [
                {
                    "parameter": {
                        "name": "id",
                        "source": 4,
                        "index": 0
                    },
                    "cmp": 6,
                    "expect": "^.+[2]$"
                }
            ],
            "strategy": 1,
            "trafficRate": 10,
            "status": 1,
            "api": 1,
            "name": "test-AB"
        },
        {
            "id": 2,
            "clusterID": 2,
            "conditions": [
                {
                    "parameter": {
                        "name": "id",
                        "source": 4,
                        "index": 0
                    },
                    "cmp": 6,
                    "expect": "^.+[2]$"
                }
            ],
            "strategy": 1,
            "trafficRate": 10,
            "status": 1,
            "api": 1,
            "name": "test-AB"
        },
        {
            "id": 3,
            "clusterID": 2,
            "conditions": [
                {
                    "parameter": {
                        "name": "id",
                        "source": 4,
                        "index": 0
                    },
                    "cmp": 6,
                    "expect": "^.+[2]$"
                }
            ],
            "strategy": 1,
            "trafficRate": 10,
            "status": 1,
            "api": 1,
            "name": "test-AB"
        }
    ]
}

Поля данных содержат коллекцию информации о маршрутах. Следующий пакет: /v1/routings?after=3&limit=3

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/fagongzi-gateway.git
git@api.gitlife.ru:oschina-mirror/fagongzi-gateway.git
oschina-mirror
fagongzi-gateway
fagongzi-gateway
master