Files
2025-08-29 22:53:34 +10:00

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
}