mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-27 19:53:41 +08:00
134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
package plg_backend_psql
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"slices"
|
|
"strings"
|
|
"time"
|
|
|
|
. "github.com/mickael-kerjean/filestash/server/common"
|
|
)
|
|
|
|
func getPath(path string) (LocationRow, error) {
|
|
l := LocationRow{}
|
|
for i, chunk := range strings.Split(path, "/") {
|
|
if i == 0 {
|
|
if chunk != "" {
|
|
return l, ErrNotValid
|
|
}
|
|
} else if i == 1 {
|
|
if strings.Contains(chunk, `"`) {
|
|
return l, ErrNotValid
|
|
}
|
|
l.table = chunk
|
|
} else if i == 2 {
|
|
l.row = strings.TrimSuffix(chunk, ".form")
|
|
} else {
|
|
return l, ErrNotValid
|
|
}
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
func processTable(ctx context.Context, db *sql.DB, table string) ([]Column, string, error) {
|
|
columns, err := _getColumns(ctx, db, table)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
key := ""
|
|
score := 0
|
|
for _, column := range columns {
|
|
if c := _calculateScore(column); c > score {
|
|
key = column.Name
|
|
score = c
|
|
}
|
|
}
|
|
if key == "" {
|
|
return columns, "", ErrNotValid
|
|
}
|
|
return columns, key, nil
|
|
}
|
|
|
|
func _getColumns(ctx context.Context, db *sql.DB, table string) ([]Column, error) {
|
|
rows, err := db.QueryContext(ctx, `
|
|
SELECT
|
|
c.column_name,
|
|
c.udt_name as type,
|
|
(c.is_nullable = 'YES') AS nullable,
|
|
(c.column_default IS NOT NULL) AS has_default,
|
|
coalesce(string_agg(tc.constraint_type, ', '), '') as constraint
|
|
FROM information_schema.columns AS c
|
|
LEFT JOIN information_schema.key_column_usage kcu USING (table_name, column_name)
|
|
LEFT JOIN information_schema.table_constraints tc USING (table_name, constraint_name)
|
|
WHERE c.table_name = $1
|
|
GROUP BY c.column_name, c.is_nullable, c.udt_name, c.column_default
|
|
ORDER BY MIN(c.ordinal_position)
|
|
`, table)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
columns := []Column{}
|
|
for rows.Next() {
|
|
var c Column
|
|
var constraints string
|
|
if err := rows.Scan(&c.Name, &c.Type, &c.Nullable, &c.Default, &constraints); err != nil {
|
|
return nil, err
|
|
}
|
|
c.Constraint = strings.Split(constraints, ", ")
|
|
c.Table = table
|
|
columns = append(columns, c)
|
|
}
|
|
return columns, rows.Close()
|
|
}
|
|
|
|
func _calculateScore(column Column) int {
|
|
scoreType := 0
|
|
scoreName := 1
|
|
if slices.Contains(column.Constraint, "PRIMARY KEY") {
|
|
scoreType = 3
|
|
} else if slices.Contains(column.Constraint, "UNIQUE") {
|
|
scoreType = 2
|
|
}
|
|
switch strings.ToLower(column.Name) {
|
|
case "name":
|
|
scoreName = 2
|
|
case "label":
|
|
scoreName = 2
|
|
case "email":
|
|
scoreName = 5
|
|
}
|
|
return scoreType * scoreName
|
|
}
|
|
|
|
func convertFromDB(val any) any {
|
|
switch tmp := val.(type) {
|
|
case []byte:
|
|
return string(tmp)
|
|
case time.Time:
|
|
return tmp.UTC().Format("2006-01-02T15:04")
|
|
}
|
|
return val
|
|
}
|
|
|
|
func createFormElement(val any, column Column) FormElement {
|
|
f := FormElement{
|
|
Type: "text",
|
|
}
|
|
switch column.Type {
|
|
case "timestamptz":
|
|
f.Type = "datetime"
|
|
case "bool":
|
|
f.Type = "boolean"
|
|
}
|
|
f.Value = convertFromDB(val)
|
|
|
|
f.Name = column.Name
|
|
f.Required = !column.Nullable && !column.Default
|
|
|
|
if strings.Contains(strings.ToLower(column.Name), "password") {
|
|
f.Type = "password"
|
|
}
|
|
return f
|
|
}
|