mirror of
git://f0xx.org/android_cast
synced 2026-07-29 05:17:39 +03:00
some candies
This commit is contained in:
@@ -27,4 +27,6 @@ return [
|
||||
],
|
||||
],
|
||||
'session_name' => 'ac_crash_sess',
|
||||
// When true, upload API may include a short error hint (do not enable on public prod long-term)
|
||||
'debug' => false,
|
||||
];
|
||||
|
||||
@@ -27,6 +27,22 @@ if ((int) ($data['schema_version'] ?? 0) !== 1) {
|
||||
try {
|
||||
ReportRepository::insert($data);
|
||||
json_out(['ok' => true, 'report_id' => $data['report_id'] ?? null]);
|
||||
} catch (PDOException $e) {
|
||||
$msg = $e->getMessage();
|
||||
if ($e->getCode() === '23000' || stripos($msg, 'UNIQUE') !== false) {
|
||||
json_out(['ok' => false, 'error' => 'duplicate report_id'], 409);
|
||||
}
|
||||
error_log('crash upload PDO: ' . $msg);
|
||||
$out = ['ok' => false, 'error' => 'store failed'];
|
||||
if (cfg('debug')) {
|
||||
$out['hint'] = $msg;
|
||||
}
|
||||
json_out($out, 500);
|
||||
} catch (Throwable $e) {
|
||||
json_out(['ok' => false, 'error' => 'store failed'], 500);
|
||||
error_log('crash upload: ' . $e->getMessage());
|
||||
$out = ['ok' => false, 'error' => 'store failed'];
|
||||
if (cfg('debug')) {
|
||||
$out['hint'] = $e->getMessage();
|
||||
}
|
||||
json_out($out, 500);
|
||||
}
|
||||
|
||||
@@ -24,13 +24,12 @@ a:hover { text-decoration: underline; }
|
||||
width: 56px;
|
||||
background: var(--surface);
|
||||
border-right: 1px solid var(--border);
|
||||
transition: width .2s ease;
|
||||
transition: width .22s ease;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.nav-pane.open,
|
||||
.nav-pane:hover { width: 72px; }
|
||||
.nav-pane.open { width: 200px; }
|
||||
.nav-handle {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
@@ -38,6 +37,7 @@ a:hover { text-decoration: underline; }
|
||||
transform: translateX(-50%);
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
transition: left .22s ease, transform .22s ease;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
@@ -55,6 +55,10 @@ a:hover { text-decoration: underline; }
|
||||
color: var(--text);
|
||||
outline: none;
|
||||
}
|
||||
.nav-pane.open .nav-handle {
|
||||
left: 8px;
|
||||
transform: none;
|
||||
}
|
||||
.nav-list {
|
||||
list-style: none;
|
||||
margin: 56px 0 0;
|
||||
@@ -72,7 +76,14 @@ a:hover { text-decoration: underline; }
|
||||
border-radius: 10px;
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
transition: background .15s ease, color .15s ease;
|
||||
transition: background .15s ease, color .15s ease, width .22s ease, padding .22s ease;
|
||||
}
|
||||
.nav-pane.open .nav-link {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
.nav-link.active,
|
||||
.nav-link:hover {
|
||||
@@ -81,15 +92,31 @@ a:hover { text-decoration: underline; }
|
||||
text-decoration: none;
|
||||
}
|
||||
.nav-label {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
max-width: 0;
|
||||
transition: opacity .18s ease, max-width .22s ease;
|
||||
}
|
||||
.nav-pane.open .nav-label {
|
||||
opacity: 1;
|
||||
max-width: 9rem;
|
||||
transition-delay: .06s;
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.nav-pane,
|
||||
.nav-link,
|
||||
.nav-label {
|
||||
transition: none;
|
||||
}
|
||||
.nav-pane.open .nav-label {
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
.nav-icon {
|
||||
display: block;
|
||||
@@ -219,6 +246,12 @@ a:hover { text-decoration: underline; }
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: var(--muted);
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.nav-pane.open .nav-user {
|
||||
align-items: stretch;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.nav-user-name {
|
||||
display: flex;
|
||||
@@ -227,6 +260,20 @@ a:hover { text-decoration: underline; }
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
color: var(--muted);
|
||||
gap: 10px;
|
||||
}
|
||||
.nav-pane.open .nav-user-name {
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
justify-content: flex-start;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.nav-pane.open .nav-link--logout {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0 12px;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
.nav-link--logout { color: var(--muted); }
|
||||
.nav-link--logout:hover { color: var(--danger); background: rgba(248, 113, 113, .12); }
|
||||
|
||||
@@ -14,6 +14,7 @@ declare(strict_types=1);
|
||||
|
||||
final class Database {
|
||||
private static ?PDO $pdo = null;
|
||||
private static bool $schemaChecked = false;
|
||||
|
||||
public static function pdo(): PDO {
|
||||
if (self::$pdo !== null) {
|
||||
@@ -45,6 +46,34 @@ final class Database {
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
self::$pdo->exec('PRAGMA foreign_keys = ON');
|
||||
self::ensureSchema();
|
||||
return self::$pdo;
|
||||
}
|
||||
|
||||
/** Idempotent: applies sql/schema.sqlite.sql if core tables are missing. */
|
||||
public static function ensureSchema(): void {
|
||||
if (self::$schemaChecked) {
|
||||
return;
|
||||
}
|
||||
self::$schemaChecked = true;
|
||||
$pdo = self::$pdo;
|
||||
if ($pdo === null) {
|
||||
return;
|
||||
}
|
||||
$need = static function (string $table) use ($pdo): bool {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$table]);
|
||||
return $stmt->fetchColumn() === false;
|
||||
};
|
||||
if (!$need('users') && !$need('reports')) {
|
||||
return;
|
||||
}
|
||||
$schemaFile = __DIR__ . '/../sql/schema.sqlite.sql';
|
||||
if (!is_readable($schemaFile)) {
|
||||
throw new RuntimeException('schema file missing: ' . $schemaFile);
|
||||
}
|
||||
$pdo->exec((string) file_get_contents($schemaFile));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user