Integrate asset metadata/storage support, TypeScript client asset APIs, docs updates, and the Leaflet demo while resolving conflicts with recent challenge IP/login persistence changes on main. Made-with: Cursor
This commit is contained in:
+124
-3
@@ -219,11 +219,20 @@ func (s *PostgresStore) DeleteCollection(id string) error {
|
||||
func (s *PostgresStore) SaveFeature(f Feature) {
|
||||
geom, _ := json.Marshal(f.Geometry)
|
||||
props, _ := json.Marshal(f.Properties)
|
||||
z := 0.0
|
||||
if len(f.Geometry.Coordinates) >= 3 {
|
||||
z = f.Geometry.Coordinates[2]
|
||||
}
|
||||
_, _ = s.db.Exec(
|
||||
`INSERT INTO features (id, collection_id, owner_key, type, geometry, properties, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT (id) DO UPDATE SET geometry = EXCLUDED.geometry, properties = EXCLUDED.properties, updated_at = EXCLUDED.updated_at`,
|
||||
`INSERT INTO features (id, collection_id, owner_key, type, geometry, properties, created_at, updated_at, geom)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, ST_SetSRID(ST_MakePoint($9, $10, $11), 4326))
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET geometry = EXCLUDED.geometry,
|
||||
properties = EXCLUDED.properties,
|
||||
updated_at = EXCLUDED.updated_at,
|
||||
geom = EXCLUDED.geom`,
|
||||
f.ID, f.CollectionID, f.OwnerKey, f.Type, geom, props, f.CreatedAt, f.UpdatedAt,
|
||||
f.Geometry.Coordinates[0], f.Geometry.Coordinates[1], z,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -289,6 +298,118 @@ func (s *PostgresStore) SaveUserLogin(ul UserLogin) {
|
||||
)
|
||||
}
|
||||
|
||||
func (s *PostgresStore) SaveAsset(a Asset) {
|
||||
_, _ = s.db.Exec(
|
||||
`INSERT INTO assets (id, owner_key, checksum, ext, kind, mime_type, size_bytes, object_key, is_public, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
||||
ON CONFLICT (id) DO UPDATE
|
||||
SET kind = EXCLUDED.kind,
|
||||
mime_type = EXCLUDED.mime_type,
|
||||
size_bytes = EXCLUDED.size_bytes,
|
||||
is_public = EXCLUDED.is_public,
|
||||
updated_at = EXCLUDED.updated_at`,
|
||||
a.ID, a.OwnerKey, a.Checksum, a.Ext, a.Kind, nullStr(a.MimeType), a.SizeBytes, a.ObjectKey, a.IsPublic, a.CreatedAt, a.UpdatedAt,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetAsset(assetID string) (Asset, error) {
|
||||
var a Asset
|
||||
var mimeType sql.NullString
|
||||
err := s.db.QueryRow(
|
||||
`SELECT id, owner_key, checksum, ext, kind, mime_type, size_bytes, object_key, is_public, created_at, updated_at
|
||||
FROM assets WHERE id = $1`,
|
||||
assetID,
|
||||
).Scan(&a.ID, &a.OwnerKey, &a.Checksum, &a.Ext, &a.Kind, &mimeType, &a.SizeBytes, &a.ObjectKey, &a.IsPublic, &a.CreatedAt, &a.UpdatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return Asset{}, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return Asset{}, err
|
||||
}
|
||||
a.MimeType = mimeType.String
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) GetAssetByOwnerChecksumExt(ownerKey, checksum, ext string) (Asset, error) {
|
||||
var a Asset
|
||||
var mimeType sql.NullString
|
||||
err := s.db.QueryRow(
|
||||
`SELECT id, owner_key, checksum, ext, kind, mime_type, size_bytes, object_key, is_public, created_at, updated_at
|
||||
FROM assets WHERE owner_key = $1 AND checksum = $2 AND ext = $3`,
|
||||
ownerKey, checksum, ext,
|
||||
).Scan(&a.ID, &a.OwnerKey, &a.Checksum, &a.Ext, &a.Kind, &mimeType, &a.SizeBytes, &a.ObjectKey, &a.IsPublic, &a.CreatedAt, &a.UpdatedAt)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return Asset{}, ErrNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return Asset{}, err
|
||||
}
|
||||
a.MimeType = mimeType.String
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) SetAssetPublic(assetID string, isPublic bool) error {
|
||||
res, err := s.db.Exec(`UPDATE assets SET is_public = $2, updated_at = NOW() WHERE id = $1`, assetID, isPublic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
if n == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) LinkAssetToFeature(featureID, assetID, name, description string) error {
|
||||
_, err := s.db.Exec(
|
||||
`INSERT INTO feature_asset_links (feature_id, asset_id, name, description)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (feature_id, asset_id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description`,
|
||||
featureID, assetID, nullStr(name), nullStr(description),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *PostgresStore) UnlinkAssetFromFeature(featureID, assetID string) error {
|
||||
res, err := s.db.Exec(`DELETE FROM feature_asset_links WHERE feature_id = $1 AND asset_id = $2`, featureID, assetID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
if n == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *PostgresStore) ListAssetsByFeature(featureID string) []FeatureAsset {
|
||||
rows, err := s.db.Query(
|
||||
`SELECT a.id, a.owner_key, a.checksum, a.ext, a.kind, COALESCE(a.mime_type, ''), a.size_bytes, a.object_key,
|
||||
a.is_public, a.created_at, a.updated_at,
|
||||
l.feature_id, COALESCE(l.name, ''), COALESCE(l.description, ''), l.created_at
|
||||
FROM feature_asset_links l
|
||||
JOIN assets a ON a.id = l.asset_id
|
||||
WHERE l.feature_id = $1
|
||||
ORDER BY l.created_at`,
|
||||
featureID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer rows.Close()
|
||||
result := make([]FeatureAsset, 0)
|
||||
for rows.Next() {
|
||||
var fa FeatureAsset
|
||||
if err := rows.Scan(
|
||||
&fa.ID, &fa.OwnerKey, &fa.Checksum, &fa.Ext, &fa.Kind, &fa.MimeType, &fa.SizeBytes, &fa.ObjectKey,
|
||||
&fa.IsPublic, &fa.CreatedAt, &fa.UpdatedAt, &fa.FeatureID, &fa.Name, &fa.Description, &fa.LinkedAt,
|
||||
); err != nil {
|
||||
return result
|
||||
}
|
||||
result = append(result, fa)
|
||||
}
|
||||
return result
|
||||
}
|
||||
func (s *PostgresStore) PruneExpired(now time.Time) {
|
||||
_, _ = s.db.Exec(`DELETE FROM challenges WHERE expires_at < $1`, now)
|
||||
_, _ = s.db.Exec(`DELETE FROM sessions WHERE expires_at < $1`, now)
|
||||
|
||||
Reference in New Issue
Block a user