refactor(auth): replace dashboard sessions with stateless tokens and session control (#6329)
* refactor(auth): replace dashboard sessions with stateless tokens * feat(auth): harden session issuance and distributed enforcement * fix(proxy): preserve trusted proxy compatibility defaults * refactor: address dashboard auth review feedback * refactor: remove classic frontend and flatten web app
This commit is contained in:
Vendored
+614
@@ -0,0 +1,614 @@
|
||||
/*
|
||||
Copyright (C) 2023-2026 QuantumNous
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
@import 'tailwindcss';
|
||||
@import 'tw-animate-css';
|
||||
@import 'shadcn/tailwind.css';
|
||||
@import '@fontsource-variable/public-sans';
|
||||
/* Editorial serif (Lora) backing the `serif` font axis and the Anthropic
|
||||
* preset's default typography. See `--font-serif` in theme.css for the
|
||||
* full Latin + CJK fallback stack and `theme-presets.css` for the cascade
|
||||
* that activates it. Loaded globally so font-switching is instantaneous
|
||||
* with no FOUT once the variable is fetched. */
|
||||
@import '@fontsource-variable/lora';
|
||||
|
||||
@import './theme.css';
|
||||
@import './theme-presets.css';
|
||||
|
||||
/* Shiki dual themes: token colors follow dark theme (pre background stays `bg-background` on the block) */
|
||||
@layer components {
|
||||
.shiki span {
|
||||
color: var(--shiki-light) !important;
|
||||
font-style: var(--shiki-light-font-style) !important;
|
||||
font-weight: var(--shiki-light-font-weight) !important;
|
||||
text-decoration: var(--shiki-light-text-decoration) !important;
|
||||
}
|
||||
|
||||
.dark .shiki span {
|
||||
color: var(--shiki-dark) !important;
|
||||
font-style: var(--shiki-dark-font-style) !important;
|
||||
font-weight: var(--shiki-dark-font-weight) !important;
|
||||
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border outline-ring/50;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--border) transparent;
|
||||
}
|
||||
html {
|
||||
@apply overflow-x-hidden font-sans;
|
||||
}
|
||||
body {
|
||||
@apply bg-background text-foreground has-[div[data-variant='inset']]:bg-sidebar min-h-svh w-full;
|
||||
/* Font is driven by the theme's font axis via `--font-body`
|
||||
* (defined in theme.css, swapped by `[data-theme-font='...']` blocks
|
||||
* in theme-presets.css). Defaults to the project's humanist sans. */
|
||||
font-family: var(--font-body);
|
||||
}
|
||||
|
||||
/* Keep sticky headers stable while primitives lock body scrolling. */
|
||||
body[data-scroll-locked] {
|
||||
overflow: unset !important;
|
||||
}
|
||||
|
||||
/* Cursor pointer for buttons */
|
||||
button:not(:disabled),
|
||||
[role='button']:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Prevent focus zoom on mobile devices */
|
||||
@media screen and (max-width: 767px) {
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Vercel Geist-style skeleton shimmer */
|
||||
.skeleton-shimmer {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--skeleton-base) 0%,
|
||||
var(--skeleton-base) 33%,
|
||||
var(--skeleton-highlight) 50%,
|
||||
var(--skeleton-base) 67%,
|
||||
var(--skeleton-base) 100%
|
||||
);
|
||||
background-size: 300% 100%;
|
||||
animation: skeleton-shimmer 1.8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes skeleton-shimmer {
|
||||
0% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Override auto-skeleton-react inline styles with Vercel shimmer */
|
||||
.auto-skeleton-fade [class^='skeleton-'] {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
var(--skeleton-base) 0%,
|
||||
var(--skeleton-base) 33%,
|
||||
var(--skeleton-highlight) 50%,
|
||||
var(--skeleton-base) 67%,
|
||||
var(--skeleton-base) 100%
|
||||
) !important;
|
||||
background-size: 300% 100% !important;
|
||||
animation: skeleton-shimmer 1.8s ease-in-out infinite !important;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.skeleton-shimmer,
|
||||
.auto-skeleton-fade [class^='skeleton-'] {
|
||||
animation: none !important;
|
||||
background: var(--skeleton-base) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@utility container {
|
||||
margin-inline: auto;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
|
||||
@utility max-w-container {
|
||||
max-width: 1280px;
|
||||
}
|
||||
|
||||
@utility max-w-container-lg {
|
||||
max-width: 1536px;
|
||||
}
|
||||
|
||||
@utility no-scrollbar {
|
||||
/* Hide scrollbar for Chrome, Safari and Opera */
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
/* Hide scrollbar for IE, Edge and Firefox */
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
|
||||
/* Tooltip content can still scroll, but should not show a distracting axis. */
|
||||
[data-slot='tooltip-content'],
|
||||
[data-slot='tooltip-content'] * {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
[data-slot='tooltip-content']::-webkit-scrollbar,
|
||||
[data-slot='tooltip-content'] *::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@utility hover-scrollbar {
|
||||
/* Hide scrollbar by default */
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: transparent transparent;
|
||||
|
||||
/* Show scrollbar on hover */
|
||||
&:hover {
|
||||
scrollbar-color: var(--border) transparent;
|
||||
}
|
||||
|
||||
/* Webkit browsers (Chrome, Safari, Edge) */
|
||||
&::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb {
|
||||
background-color: var(--border);
|
||||
}
|
||||
|
||||
&:hover::-webkit-scrollbar-thumb:hover {
|
||||
background-color: color-mix(in oklch, var(--border) 60%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
@utility faded-bottom {
|
||||
@apply after:pointer-events-none after:absolute after:start-0 after:bottom-0 after:hidden after:h-32 after:w-full after:rounded-b-2xl after:bg-[linear-gradient(180deg,_transparent_10%,_var(--background)_70%)] md:after:block;
|
||||
}
|
||||
|
||||
.CollapsibleContent {
|
||||
overflow: hidden;
|
||||
}
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.CollapsibleContent[data-open] {
|
||||
animation: slideDown 300ms ease-out;
|
||||
}
|
||||
.CollapsibleContent:not([data-open]) {
|
||||
animation: slideUp 300ms ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
height: 0;
|
||||
}
|
||||
to {
|
||||
height: var(--collapsible-panel-height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
height: var(--collapsible-panel-height);
|
||||
}
|
||||
to {
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Launch UI Animations and Effects - Matching Template Exactly */
|
||||
@layer utilities {
|
||||
/* Gradient utilities */
|
||||
.bg-radial {
|
||||
background-image: radial-gradient(var(--tw-gradient-stops));
|
||||
}
|
||||
|
||||
/* Glass morphism effects - matching Launch UI template */
|
||||
.glass-1 {
|
||||
@apply border-border from-card/80 to-card/40 dark:border-border/10 dark:border-b-border/5 dark:border-t-border/20 dark:from-card/5 dark:to-card/0 border bg-linear-to-b;
|
||||
}
|
||||
|
||||
.glass-2 {
|
||||
@apply border-border from-card/100 to-card/80 dark:border-border/10 dark:border-b-border/5 dark:border-t-border/20 dark:from-card/10 dark:to-card/5 border bg-linear-to-b;
|
||||
}
|
||||
|
||||
.glass-3 {
|
||||
@apply border-border from-card/30 to-card/20 dark:border-border/10 dark:border-t-border/20 dark:border-b-border/5 dark:from-primary/5 dark:to-primary/2 border bg-linear-to-b;
|
||||
}
|
||||
|
||||
.glass-4 {
|
||||
@apply border-border border-b-input/90 from-card/60 to-card/20 dark:border-border/10 dark:border-t-border/30 dark:from-primary/10 dark:to-primary/5 border bg-linear-to-b dark:border-b-0;
|
||||
}
|
||||
|
||||
.glass-5 {
|
||||
@apply border-border border-b-input from-card/100 to-card/20 dark:border-border/10 dark:border-t-border/30 dark:from-primary/15 dark:to-primary/5 border bg-linear-to-b dark:border-b-0;
|
||||
}
|
||||
|
||||
/* Fade effects - matching Launch UI template */
|
||||
.fade-x {
|
||||
mask-image: linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
black 25%,
|
||||
black 75%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.fade-y {
|
||||
mask-image: linear-gradient(
|
||||
to top,
|
||||
transparent 0%,
|
||||
black 25%,
|
||||
black 75%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
.fade-top {
|
||||
mask-image: linear-gradient(to bottom, transparent 0%, black 35%);
|
||||
}
|
||||
|
||||
.fade-bottom {
|
||||
mask-image: linear-gradient(to top, transparent 0%, black 35%);
|
||||
}
|
||||
|
||||
.fade-top-lg {
|
||||
mask-image: linear-gradient(to bottom, transparent 15%, black 100%);
|
||||
}
|
||||
|
||||
.fade-bottom-lg {
|
||||
mask-image: linear-gradient(to top, transparent 15%, black 100%);
|
||||
}
|
||||
|
||||
.fade-left {
|
||||
mask-image: linear-gradient(to right, transparent 0%, black 35%);
|
||||
}
|
||||
|
||||
.fade-right {
|
||||
mask-image: linear-gradient(to left, transparent 0%, black 35%);
|
||||
}
|
||||
|
||||
.fade-left-lg {
|
||||
mask-image: linear-gradient(to right, transparent 15%, black 100%);
|
||||
}
|
||||
|
||||
.fade-right-lg {
|
||||
mask-image: linear-gradient(to left, transparent 15%, black 100%);
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.animate-appear {
|
||||
animation: appear 0.6s forwards ease-out;
|
||||
}
|
||||
|
||||
.animate-appear-zoom {
|
||||
animation: appear-zoom 0.6s forwards ease-out;
|
||||
}
|
||||
|
||||
.animation-delay-100 {
|
||||
animation-delay: 100ms;
|
||||
}
|
||||
|
||||
.animation-delay-300 {
|
||||
animation-delay: 300ms;
|
||||
}
|
||||
|
||||
.animation-delay-700 {
|
||||
animation-delay: 700ms;
|
||||
}
|
||||
|
||||
.animation-delay-1000 {
|
||||
animation-delay: 1000ms;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(1rem);
|
||||
filter: blur(0.5rem);
|
||||
}
|
||||
50% {
|
||||
filter: blur(0);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes appear-zoom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Vertical scroll animation for icon lists */
|
||||
@keyframes scroll-up {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
.animate-scroll-up {
|
||||
animation: scroll-up 20s linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.animate-scroll-down {
|
||||
animation: scroll-up 20s linear infinite reverse;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Pause animation on hover */
|
||||
.scroll-container:hover .animate-scroll-up,
|
||||
.scroll-container:hover .animate-scroll-down {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.animate-appear,
|
||||
.animate-appear-zoom,
|
||||
.animate-scroll-up,
|
||||
.animate-scroll-down,
|
||||
[data-slot='table'] tbody tr {
|
||||
animation: none !important;
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Landing page scroll-triggered animations ── */
|
||||
@keyframes landing-fade-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes landing-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@keyframes landing-scale-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes landing-fade-left {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@keyframes landing-fade-right {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.landing-animate-fade-up {
|
||||
animation: landing-fade-up 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
.landing-animate-fade-in {
|
||||
animation: landing-fade-in 0.6s ease-out both;
|
||||
}
|
||||
.landing-animate-scale-in {
|
||||
animation: landing-scale-in 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
.landing-animate-fade-left {
|
||||
animation: landing-fade-left 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
.landing-animate-fade-right {
|
||||
animation: landing-fade-right 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.landing-animate-fade-up,
|
||||
.landing-animate-fade-in,
|
||||
.landing-animate-scale-in,
|
||||
.landing-animate-fade-left,
|
||||
.landing-animate-fade-right {
|
||||
animation: none !important;
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Micro-interactions — Vercel-style subtle hover/active feedback */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
[data-slot='card']:not([data-card-hover='false']) {
|
||||
transition:
|
||||
transform 150ms ease,
|
||||
box-shadow 150ms ease;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
[data-slot='card']:not([data-card-hover='false']):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgb(0 0 0 / 0.06);
|
||||
}
|
||||
|
||||
.dark [data-slot='card']:not([data-card-hover='false']):hover {
|
||||
box-shadow: 0 4px 12px rgb(0 0 0 / 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
button:not(:disabled):active,
|
||||
[role='button']:not(:disabled):active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
[data-slot='table'] tr {
|
||||
transition: background-color 120ms ease;
|
||||
}
|
||||
|
||||
/* Table row stagger-in animation */
|
||||
[data-slot='table'] tbody tr {
|
||||
animation: tableRowEnter 0.2s cubic-bezier(0.33, 1, 0.68, 1) both;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(2) {
|
||||
animation-delay: 25ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(3) {
|
||||
animation-delay: 50ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(4) {
|
||||
animation-delay: 75ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(5) {
|
||||
animation-delay: 100ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(6) {
|
||||
animation-delay: 125ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(7) {
|
||||
animation-delay: 150ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(8) {
|
||||
animation-delay: 175ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(9) {
|
||||
animation-delay: 200ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(10) {
|
||||
animation-delay: 225ms;
|
||||
}
|
||||
[data-slot='table'] tbody tr:nth-child(n + 11) {
|
||||
animation-delay: 250ms;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes tableRowEnter {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Tabular numbers for data-heavy contexts */
|
||||
[data-slot='table'] td,
|
||||
.tabular-nums {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* Long list rendering optimization */
|
||||
.content-auto {
|
||||
content-visibility: auto;
|
||||
contain-intrinsic-size: 0 80px;
|
||||
}
|
||||
|
||||
/* ── Hero terminal demo animations ── */
|
||||
@keyframes terminal-demo-blink {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.terminal-demo-blink {
|
||||
animation: terminal-demo-blink 1s step-end infinite;
|
||||
}
|
||||
|
||||
@keyframes terminal-demo-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.terminal-demo-spin {
|
||||
animation: terminal-demo-spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes terminal-demo-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
.terminal-demo-pulse {
|
||||
animation: terminal-demo-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.terminal-demo-blink,
|
||||
.terminal-demo-spin,
|
||||
.terminal-demo-pulse {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user