Initial commit: WordPress wp-content (themes, plugins, languages)

- Theme: momentry (custom theme with REST API routes)
- Plugins: code-snippets (contains all API proxies)
- Languages: zh_TW translations
- Excludes: cache, backups, uploads, logs
This commit is contained in:
OpenCode
2026-05-29 19:07:56 +08:00
commit 09ef1f000f
6521 changed files with 867163 additions and 0 deletions

View File

@@ -0,0 +1,382 @@
<?php
declare(strict_types=1);
namespace Momentry;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;
use PDO;
defined('ABSPATH') || exit;
class Identity_API {
private PDO $db;
private string $schema;
public function __construct() {
$this->db = Database::get_instance();
$this->schema = Database::get_schema();
}
public function register_routes(): void {
register_rest_route('momentry/v1', '/identities', [
'methods' => 'GET',
'callback' => [$this, 'get_identities'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route('momentry/v1', '/identities/(?P<uuid>[a-f0-9\-]{36})', [
'methods' => 'GET',
'callback' => [$this, 'get_identity_detail'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route('momentry/v1', '/identities/(?P<uuid>[a-f0-9\-]{36})/angle-coverage', [
'methods' => 'GET',
'callback' => [$this, 'get_angle_coverage'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route('momentry/v1', '/identities/(?P<uuid>[a-f0-9\-]{36})/body-actions', [
'methods' => 'GET',
'callback' => [$this, 'get_body_actions'],
'permission_callback' => [$this, 'check_permission'],
]);
register_rest_route('momentry/v1', '/identities/(?P<uuid>[a-f0-9\-]{36})/reference-vectors', [
'methods' => 'GET',
'callback' => [$this, 'get_reference_vectors'],
'permission_callback' => [$this, 'check_permission'],
]);
}
public function check_permission(): bool {
return current_user_can('read') || $this->validate_api_key();
}
private function validate_api_key(): bool {
$api_key = $_SERVER['HTTP_X_MOMENTRY_API_KEY'] ?? '';
$valid_key = getenv('MOMENTRY_API_KEY');
if (!$valid_key) {
return false;
}
return hash_equals($valid_key, $api_key);
}
public function get_identities(WP_REST_Request $request): WP_REST_Response {
$page = (int) $request->get_param('page') ?: 1;
$per_page = (int) $request->get_param('per_page') ?: 50;
$offset = ($page - 1) * $per_page;
$search = $request->get_param('search');
$source = $request->get_param('source');
$sql = "SELECT
uuid,
name,
identity_type,
source,
tmdb_id,
created_at,
reference_data->>'total_references' as total_references,
reference_data->>'quality_avg' as quality_avg,
reference_data->'trace_stats'->>'duration_seconds' as trace_duration,
reference_data->'trace_stats'->>'avg_confidence' as trace_confidence,
reference_data->'trace_stats'->>'total_appearances' as trace_appearances
FROM {$this->schema}.identities";
$where = [];
$params = [];
if ($search) {
$where[] = "name ILIKE ?";
$params[] = "%{$search}%";
}
if ($source) {
$where[] = "source = ?";
$params[] = $source;
}
if (!empty($where)) {
$sql .= " WHERE " . implode(' AND ', $where);
}
$sql .= " ORDER BY created_at DESC LIMIT ? OFFSET ?";
$params[] = $per_page;
$params[] = $offset;
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
$identities = $stmt->fetchAll(PDO::FETCH_ASSOC);
$count_sql = "SELECT COUNT(*) FROM {$this->schema}.identities";
if (!empty($where)) {
$count_sql .= " WHERE " . implode(' AND ', $where);
}
$stmt = $this->db->prepare($count_sql);
$stmt->execute(array_slice($params, 0, -2));
$total = (int) $stmt->fetchColumn();
$identities = array_map(function ($identity) {
return $this->format_identity_list_item($identity);
}, $identities);
return new WP_REST_Response([
'success' => true,
'data' => $identities,
'pagination' => [
'page' => $page,
'per_page' => $per_page,
'total' => $total,
'total_pages' => ceil($total / $per_page),
],
]);
}
public function get_identity_detail(WP_REST_Request $request): WP_REST_Response|WP_Error {
$uuid = $request['uuid'];
$sql = "SELECT
uuid,
name,
identity_type,
source,
tmdb_id,
tmdb_profile,
reference_data,
created_at,
updated_at
FROM {$this->schema}.identities
WHERE uuid = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$uuid]);
$identity = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$identity) {
return new WP_Error(
'not_found',
'Identity not found',
['status' => 404]
);
}
return new WP_REST_Response([
'success' => true,
'data' => $this->format_identity_detail($identity),
]);
}
public function get_angle_coverage(WP_REST_Request $request): WP_REST_Response|WP_Error {
$uuid = $request['uuid'];
$sql = "SELECT reference_data FROM {$this->schema}.identities WHERE uuid = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$uuid]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return new WP_Error(
'not_found',
'Identity not found',
['status' => 404]
);
}
$reference_data = json_decode($result['reference_data'], true);
$angle_coverage = $this->calculate_angle_coverage($reference_data);
return new WP_REST_Response([
'success' => true,
'data' => [
'uuid' => $uuid,
'angles' => $angle_coverage['angles'],
'coverage_score' => $angle_coverage['score'],
'recommendation' => $angle_coverage['recommendation'],
],
]);
}
public function get_body_actions(WP_REST_Request $request): WP_REST_Response|WP_Error {
$uuid = $request['uuid'];
$sql = "SELECT
i.uuid,
i.name,
i.reference_data
FROM {$this->schema}.identities i
WHERE i.uuid = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$uuid]);
$identity = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$identity) {
return new WP_Error(
'not_found',
'Identity not found',
['status' => 404]
);
}
$reference_data = json_decode($identity['reference_data'], true);
$body_actions = $reference_data['body_actions'] ?? [];
$action_statistics = $reference_data['action_statistics'] ?? [];
return new WP_REST_Response([
'success' => true,
'data' => [
'uuid' => $uuid,
'name' => $identity['name'],
'actions' => $body_actions,
'statistics' => $action_statistics,
],
]);
}
public function get_reference_vectors(WP_REST_Request $request): WP_REST_Response|WP_Error {
$uuid = $request['uuid'];
$sql = "SELECT reference_data FROM {$this->schema}.identities WHERE uuid = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$uuid]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return new WP_Error(
'not_found',
'Identity not found',
['status' => 404]
);
}
$reference_data = json_decode($result['reference_data'], true);
$face_embeddings = $reference_data['face_embeddings'] ?? [];
$vectors = array_map(function ($embedding) {
return [
'angle' => $embedding['angle'] ?? 'unknown',
'frame' => $embedding['frame'] ?? null,
'quality_score' => $embedding['quality_score'] ?? 0,
'pitch' => $embedding['pitch'] ?? 'neutral',
'pose_confidence' => $embedding['pose_confidence'] ?? 0,
'detection_confidence' => $embedding['detection_confidence'] ?? 0,
'attributes' => [
'age' => $embedding['attributes']['age'] ?? null,
'gender' => $embedding['attributes']['gender'] ?? null,
],
];
}, $face_embeddings);
return new WP_REST_Response([
'success' => true,
'data' => [
'uuid' => $uuid,
'total_vectors' => count($vectors),
'vectors' => $vectors,
],
]);
}
private function format_identity_list_item(array $identity): array {
return [
'uuid' => $identity['uuid'],
'name' => $identity['name'],
'type' => $identity['identity_type'],
'source' => $identity['source'],
'total_references' => (int) ($identity['total_references'] ?? 0),
'quality_avg' => $identity['quality_avg'] ? round((float) $identity['quality_avg'], 3) : null,
'trace_duration' => $identity['trace_duration'] ? round((float) $identity['trace_duration'], 2) : null,
'trace_confidence' => $identity['trace_confidence'] ? round((float) $identity['trace_confidence'], 4) : null,
'tmdb_id' => $identity['tmdb_id'],
'created_at' => $identity['created_at'],
];
}
private function format_identity_detail(array $identity): array {
$reference_data = json_decode($identity['reference_data'], true);
return [
'uuid' => $identity['uuid'],
'name' => $identity['name'],
'type' => $identity['identity_type'],
'source' => $identity['source'],
'tmdb_id' => $identity['tmdb_id'],
'tmdb_profile' => $identity['tmdb_profile'],
'reference_vectors' => [
'total' => $reference_data['total_references'] ?? 0,
'angles' => $reference_data['angles_covered'] ?? [],
'quality_avg' => $reference_data['quality_avg'] ?? null,
],
'trace_stats' => $reference_data['trace_stats'] ?? null,
'created_at' => $identity['created_at'],
'updated_at' => $identity['updated_at'],
];
}
private function calculate_angle_coverage(?array $reference_data): array {
if (!$reference_data) {
return [
'angles' => [],
'score' => 0,
'recommendation' => 'No reference data available',
];
}
$face_embeddings = $reference_data['face_embeddings'] ?? [];
$required_angles = ['frontal', 'three_quarter', 'profile_left', 'profile_right'];
$angle_stats = [];
foreach ($required_angles as $angle) {
$angle_stats[$angle] = [
'count' => 0,
'quality_sum' => 0,
'status' => 'missing',
];
}
foreach ($face_embeddings as $embedding) {
$angle = $embedding['angle'] ?? 'unknown';
$quality = $embedding['quality_score'] ?? 0;
if (isset($angle_stats[$angle])) {
$angle_stats[$angle]['count']++;
$angle_stats[$angle]['quality_sum'] += $quality;
}
}
$covered_count = 0;
$total_quality = 0;
foreach ($angle_stats as $angle => &$stats) {
if ($stats['count'] > 0) {
$stats['quality_avg'] = round($stats['quality_sum'] / $stats['count'], 3);
$stats['status'] = $stats['count'] > 10 ? 'dominant' : 'present';
$covered_count++;
$total_quality += $stats['quality_avg'];
}
unset($stats['quality_sum']);
}
$coverage_score = round(($covered_count / count($required_angles)) * 100);
$avg_quality = $covered_count > 0 ? round($total_quality / $covered_count, 3) : 0;
$missing_angles = array_keys(array_filter($angle_stats, fn($s) => $s['count'] === 0));
$recommendation = 'Excellent coverage!';
if (count($missing_angles) > 0) {
$recommendation = 'Add ' . implode(', ', $missing_angles) . ' angle(s) for better coverage';
}
return [
'angles' => $angle_stats,
'score' => $coverage_score,
'quality_avg' => $avg_quality,
'recommendation' => $recommendation,
];
}
}