Compare commits

..

2 Commits

Author SHA1 Message Date
2ab29d1e02 合併 2023-05-11 07:50:56 +08:00
69973202e0 服務器狀態 2023-04-29 19:16:03 +08:00
9 changed files with 376 additions and 27 deletions

View File

@@ -38,6 +38,7 @@ POST | /api/{name} | 篩選:創建新對象
- [ ] /api/models/{model_id} [#模型詳情](#模型詳情) - [ ] /api/models/{model_id} [#模型詳情](#模型詳情)
- [ ] /api/images/{image_id} [#圖片詳情](#圖片詳情) - [ ] /api/images/{image_id} [#圖片詳情](#圖片詳情)
- [ ] /api/params/{params_id} [#參數詳情](#參數詳情) - [ ] /api/params/{params_id} [#參數詳情](#參數詳情)
- [ ] /api/server/{server_id} [#服務詳情](#服務詳情)
詳情接口-請求方式: 詳情接口-請求方式:
@@ -200,38 +201,38 @@ GET /api/images
列表视图:(输出控制) 列表视图:(输出控制)
Method | URL | Info | Status Method | URL | Info
-------|--------------------------------|-----------------------------------|-------- -------|--------------------------------|----------------------------------
GET | /api/images | 标准顺序查询 | ok GET | /api/images | 标准顺序查询
GET | /api/images?page=1&pageSize=20 | 指定页码和指定分页大小 | ok GET | /api/images?page=1&pageSize=20 | 指定页码和指定分页大小
DELETE | /api/images/{image_id} | 刪除指定圖片 DELETE | /api/images/{image_id} | 刪除指定圖片
筛选规则:(数据过滤) 筛选规则:(数据过滤)
Method | URL | Info | Statu Method | URL | Info
-------|--------------------------------|-----------------------------------|-------- -------|--------------------------------|----------------------------------
GET | /api/images?user=1234 | 筛选指定某用户发表的图片 | GET | /api/images?user=1234 | 筛选指定某用户发表的图片
GET | /api/images?choice=1234 | 筛选指定精选集下的图片 | GET | /api/images?choice=1234 | 筛选指定精选集下的图片
GET | /api/images?like=1234 | 筛选指定用户点赞的图片 | GET | /api/images?like=1234 | 筛选指定用户点赞的图片
GET | /api/images?tag=1234 | 筛选含有指定标签的图片 | GET | /api/images?tag=1234 | 筛选含有指定标签的图片
GET | /api/images?tag=1234,1235 | 筛选含有多个标签之一的图片(并集) | GET | /api/images?tag=1234,1235 | 筛选含有多个标签之一的图片(并集)
GET | /api/images?tag=1234&tag=1235 | 筛选含有指定多个标签的图片(交集) | GET | /api/images?tag=1234&tag=1235 | 筛选含有指定多个标签的图片(交集)
GET | /api/images?user=1234&tag=123 | 筛选指定用户的指定标签图片(交集) | GET | /api/images?user=1234&tag=123 | 筛选指定用户的指定标签图片(交集)
GET | /api/images?date=20220214+ | 时间范围(之后) | GET | /api/images?date=20220214+ | 时间范围(之后)
GET | /api/images?date=20220214- | 时间范围(之前) | GET | /api/images?date=20220214- | 时间范围(之前)
GET | /api/images?date=2022~2023 | 时间范围(之间) | GET | /api/images?date=2022~2023 | 时间范围(之间)
排序规则:(权重强化) 排序规则:(权重强化)
Method | URL | Info | Status Method | URL | Info
-------|--------------------------------|-----------------------------------|-------- -------|--------------------------------|----------------------------------
GET | /api/images?similar=1234 | 根据指定图片的相似图片(指定图片ID) | ok GET | /api/images?similar=1234 | 根据指定图片的相似图片(指定图片ID)
GET | /api/images?sort=date+ | 排序规则(相似图片查询时此项无效) | GET | /api/images?sort=date+ | 排序规则(相似图片查询时此项无效)
GET | /api/images?sort=like | 根据用户偏好推荐(指定用户的偏好) | GET | /api/images?sort=like | 根据用户偏好推荐(指定用户的偏好)
GET | /api/images?sort=history | 根据浏览记录推荐(指定用户的记录) | GET | /api/images?sort=history | 根据浏览记录推荐(指定用户的记录)
GET | /api/images?sort=choice | 根据精选集推荐(指定精选集ID,取一组权重) | GET | /api/images?sort=choice | 根据精选集推荐(指定精选集ID,取一组权重)
* 注意, 筛选规则为多条件取交集, 单条件的复数取并集 * 注意, 筛选规则为多条件取交集, 单条件的复数取并集
* 权重强化属于排序规则而非过滤规则 * 权重强化属于排序规则而非过滤规则
@@ -257,7 +258,7 @@ GET /api/models
篩選規則:(數據過濾) 篩選規則:(數據過濾)
Method | URL | Info Method | URL | Info
-------|--------------------------------|------------------ -------|--------------------------------|----------------------------------
GET | /api/models | 獲取所有模型 GET | /api/models | 獲取所有模型
GET | /api/models?user=1234 | 僅取指定用戶的(按用戶ID過濾) GET | /api/models?user=1234 | 僅取指定用戶的(按用戶ID過濾)
GET | /api/models?tag=xxx | 按標籤分類篩選 GET | /api/models?tag=xxx | 按標籤分類篩選

View File

@@ -61,7 +61,9 @@ func init() {
CREATE TABLE IF NOT EXISTS users( CREATE TABLE IF NOT EXISTS users(
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT, name TEXT,
email TEXT,
password TEXT, password TEXT,
slat TEXT,
created_at TEXT, created_at TEXT,
updated_at TEXT updated_at TEXT
); );

View File

@@ -21,6 +21,29 @@ type Model struct {
UserID int `json:"user_id"` UserID int `json:"user_id"`
} }
func (model *Model) SendToTrain() error {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return err
}
defer db.Close()
stmt, err := db.Prepare("UPDATE models SET status = ?, progress = ?, updated_at = ? WHERE id = ?")
if err != nil {
log.Println(err)
return err
}
defer stmt.Close()
_, err = stmt.Exec(model.Status, model.Progress, model.UpdatedAt, model.ID)
if err != nil {
log.Println(err)
return err
}
// TODO: 創建一個新線程管理訓練任務
// 將任務放入隊列中, 將自動回調更新任務狀態
return nil
}
func (model *Model) Create() error { func (model *Model) Create() error {
db, err := configs.GetDB() db, err := configs.GetDB()
if err != nil { if err != nil {

141
models/Server.go Normal file
View File

@@ -0,0 +1,141 @@
package models
import (
"log"
"main/configs"
)
type Server struct {
ID int `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // (訓練|推理)
IP string `json:"ip"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
func (server *Server) Create() error {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return err
}
defer db.Close()
stmt, err := db.Prepare("INSERT INTO servers(name, ip, port, username, password, created_at, updated_at) values(?, ?, ?, ?, ?, ?, ?)")
if err != nil {
log.Println(err)
return err
}
defer stmt.Close()
result, err := stmt.Exec(server.Name, server.IP, server.Port, server.Username, server.Password, server.CreatedAt, server.UpdatedAt)
if err != nil {
log.Println(err)
return err
}
id, err := result.LastInsertId()
if err != nil {
return err
}
server.ID = int(id)
return nil
}
func (server *Server) Delete() error {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return err
}
defer db.Close()
stmt, err := db.Prepare("DELETE FROM servers WHERE id = ?")
if err != nil {
log.Println(err)
return err
}
defer stmt.Close()
_, err = stmt.Exec(server.ID)
if err != nil {
log.Println(err)
return err
}
return nil
}
func (server *Server) Update() error {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return err
}
defer db.Close()
stmt, err := db.Prepare("UPDATE servers SET name = ?, ip = ?, port = ?, username = ?, password = ?, updated_at = ? WHERE id = ?")
if err != nil {
log.Println(err)
return err
}
defer stmt.Close()
_, err = stmt.Exec(server.Name, server.IP, server.Port, server.Username, server.Password, server.UpdatedAt, server.ID)
if err != nil {
log.Println(err)
return err
}
return nil
}
func (server *Server) Get() error {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return err
}
defer db.Close()
err = db.QueryRow("SELECT id, name, ip, port, username, password, created_at, updated_at FROM servers WHERE id = ?", server.ID).Scan(&server.ID, &server.Name, &server.IP, &server.Port, &server.Username, &server.Password, &server.CreatedAt, &server.UpdatedAt)
if err != nil {
log.Println(err)
return err
}
return nil
}
func QueryServers(page int, pagesize int) (servers []interface{}) {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return
}
defer db.Close()
rows, err := db.Query("SELECT id, name, ip, port, username, password, created_at, updated_at FROM servers ORDER BY id DESC LIMIT ?, ?", page*pagesize, pagesize)
if err != nil {
log.Println(err)
return
}
defer rows.Close()
for rows.Next() {
server := Server{}
err := rows.Scan(&server.ID, &server.Name, &server.IP, &server.Port, &server.Username, &server.Password, &server.CreatedAt, &server.UpdatedAt)
if err != nil {
log.Println(err)
continue
}
servers = append(servers, server)
}
return
}
func CountServers() (count int) {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return
}
defer db.Close()
err = db.QueryRow("SELECT COUNT(*) FROM servers").Scan(&count)
if err != nil {
log.Println(err)
return
}
return
}

View File

@@ -3,6 +3,9 @@ package models
import ( import (
"log" "log"
"main/configs" "main/configs"
"net/http"
"strconv"
"time"
) )
type Task struct { type Task struct {
@@ -16,6 +19,49 @@ type Task struct {
UserID int `json:"user_id"` UserID int `json:"user_id"`
} }
// 推理任務
func startInferenceTask(task *Task) {
// 獲取一臺可用的 GPU 資源
// ...
// 執行推理任務
// ...
// 更新任務狀態
task.Status = "running"
task.Progress = 0
task.Update()
// 監聽任務狀態
for {
// 延遲 1 秒
time.Sleep(1 * time.Second)
// 查詢任務狀態
resp, err := http.Get("http://localhost:5000/api/v1/tasks/" + strconv.Itoa(task.ID))
if err != nil {
log.Println(err)
continue
}
defer resp.Body.Close()
// 解析任務狀態
// ...
// 更新任務狀態
task.Progress = 100
task.Status = "success"
task.Update()
// 任務結束判定
if task.Progress == 100 {
break
}
}
}
func (task *Task) Create() error { func (task *Task) Create() error {
db, err := configs.GetDB() db, err := configs.GetDB()
if err != nil { if err != nil {

View File

@@ -9,6 +9,8 @@ type User struct {
ID int `json:"id"` ID int `json:"id"`
Name string `json:"name"` Name string `json:"name"`
Email string `json:"email"` Email string `json:"email"`
Password string `json:"password"`
Slat string `json:"slat"`
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"` UpdatedAt string `json:"updated_at"`
} }
@@ -136,3 +138,43 @@ func (user *User) GetByEmail() error {
} }
return nil return nil
} }
func QueryUsers(page, pagesize int) (list []interface{}) {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return
}
defer db.Close()
rows, err := db.Query("SELECT id, name, email, created_at, updated_at FROM users LIMIT ?, ?", (page-1)*pagesize, pagesize)
if err != nil {
log.Println(err)
return
}
defer rows.Close()
for rows.Next() {
var user User
err := rows.Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt, &user.UpdatedAt)
if err != nil {
log.Println(err)
return
}
list = append(list, user)
}
return
}
func CountUsers() (count int) {
db, err := configs.GetDB()
if err != nil {
log.Println(err)
return
}
defer db.Close()
err = db.QueryRow("SELECT COUNT(*) FROM users").Scan(&count)
if err != nil {
log.Println(err)
return
}
return
}

View File

@@ -81,18 +81,44 @@ func ModelItemGet(w http.ResponseWriter, r *http.Request) {
} }
func ModelItemPatch(w http.ResponseWriter, r *http.Request) { func ModelItemPatch(w http.ResponseWriter, r *http.Request) {
var model models.Model // 取出原始数据
model := models.Model{ID: utils.ParamInt(mux.Vars(r)["id"], 0)}
if err := model.Get(); err != nil {
w.WriteHeader(http.StatusNotFound)
return
}
// 取出更新数据
var model_new models.Model
body, err := ioutil.ReadAll(r.Body) body, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
} }
defer r.Body.Close() defer r.Body.Close()
if err = json.Unmarshal(body, &model); err != nil { if err = json.Unmarshal(body, &model_new); err != nil {
log.Println(err) log.Println(err)
return return
} }
model.ID = utils.ParamInt(mux.Vars(r)["id"], 0)
// 字段不爲空且不等於原始數據時更新
if model_new.Name != "" && model_new.Name != model.Name {
model.Name = model_new.Name
}
if model_new.Type != "" && model_new.Type != model.Type {
model.Type = model_new.Type
}
if model_new.Status != "" && model_new.Status != model.Status {
model.Status = model_new.Status
// 如果狀態被改變爲 ready, 將模型發送到訓練隊列
if model.Status == "ready" {
model.SendToTrain()
}
}
if model_new.Image != "" && model_new.Image != model.Image {
model.Image = model_new.Image
}
model.Update() model.Update()
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(utils.ToJSON(model)) w.Write(utils.ToJSON(model))

50
routers/servers.go Normal file
View File

@@ -0,0 +1,50 @@
package routers
import (
"main/models"
"main/utils"
"net/http"
"github.com/gorilla/mux"
)
func ServersGet(w http.ResponseWriter, r *http.Request) {
var listview models.ListView
listview.Page = utils.ParamInt(r.URL.Query().Get("page"), 1)
listview.PageSize = utils.ParamInt(r.URL.Query().Get("pageSize"), 10)
listview.List = models.QueryServers(listview.Page, listview.PageSize)
listview.Total = models.CountServers()
listview.Next = listview.Page*listview.PageSize < listview.Total
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(listview.ToJSON())
}
func ServersPost(w http.ResponseWriter, r *http.Request) {
var server models.Server
server.Create()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(utils.ToJSON(server))
}
func ServersItemGet(w http.ResponseWriter, r *http.Request) {
server := models.Server{ID: utils.ParamInt(mux.Vars(r)["id"], 0)}
server.Get()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(utils.ToJSON(server))
}
func ServersItemPatch(w http.ResponseWriter, r *http.Request) {
server := models.Server{ID: utils.ParamInt(mux.Vars(r)["id"], 0)}
server.Get()
server.Update()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(utils.ToJSON(server))
}
func ServersItemDelete(w http.ResponseWriter, r *http.Request) {
server := models.Server{ID: utils.ParamInt(mux.Vars(r)["id"], 0)}
server.Get()
server.Delete()
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(utils.ToJSON(server))
}

18
routers/users.go Normal file
View File

@@ -0,0 +1,18 @@
package routers
import (
"main/models"
"main/utils"
"net/http"
)
func UsersGet(w http.ResponseWriter, r *http.Request) {
var listview models.ListView
listview.Page = utils.ParamInt(r.URL.Query().Get("page"), 1)
listview.PageSize = utils.ParamInt(r.URL.Query().Get("pageSize"), 10)
listview.List = models.QueryUsers(listview.Page, listview.PageSize)
listview.Total = models.CountUsers()
listview.Next = listview.Page*listview.PageSize < listview.Total
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Write(listview.ToJSON())
}