serve static assets from local public/ instead of static.officer.dev, simplify gmail history 404/410 handling
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1 +1 @@
|
|||||||
{"name":"Officer Dev","short_name":"Officer","icons":[{"src":"https://static.officer.dev/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"https://static.officer.dev/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#1F2620","background_color":"#1F2620","display":"standalone"}
|
{"name":"Officer Dev","short_name":"Officer","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#1F2620","background_color":"#1F2620","display":"standalone"}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { PixelGrid } from '../../workspaces/components/PixelGrid';
|
|||||||
import { DuckAvatar } from './DuckAvatar';
|
import { DuckAvatar } from './DuckAvatar';
|
||||||
import { WaitlistForm } from './WaitlistForm';
|
import { WaitlistForm } from './WaitlistForm';
|
||||||
|
|
||||||
const landscapeBg = 'https://static.officer.dev/landscape1.webp';
|
const landscapeBg = '/landscape1.webp';
|
||||||
const logoSrc = 'https://static.officer.dev/officer-logo.svg';
|
const logoSrc = '/officer-logo.svg';
|
||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { OrbitControls, useGLTF, useAnimations } from '@react-three/drei';
|
|||||||
import { useEffect, useState, useRef } from 'react';
|
import { useEffect, useState, useRef } from 'react';
|
||||||
|
|
||||||
function DuckModel({ onLoaded }: { onLoaded: () => void }) {
|
function DuckModel({ onLoaded }: { onLoaded: () => void }) {
|
||||||
const character = useGLTF('https://static.officer.dev/duck3D/Character_output.glb');
|
const character = useGLTF('/duck3D/Character_output.glb');
|
||||||
const animations = useGLTF('https://static.officer.dev/duck3D/Meshy_Merged_Animations.glb');
|
const animations = useGLTF('/duck3D/Meshy_Merged_Animations.glb');
|
||||||
const meshRef = useRef<any>(null);
|
const meshRef = useRef<any>(null);
|
||||||
const { actions } = useAnimations(animations.animations, meshRef);
|
const { actions } = useAnimations(animations.animations, meshRef);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<meta property="og:title" content="Officer Dev (Alpha)" />
|
<meta property="og:title" content="Officer Dev (Alpha)" />
|
||||||
<meta property="og:description" content="Your all-purpose AI operating system. Personal AI assistant, terminal, file browser, code editor, and customizable dashboards — all self-hosted." />
|
<meta property="og:description" content="Your all-purpose AI operating system. Personal AI assistant, terminal, file browser, code editor, and customizable dashboards — all self-hosted." />
|
||||||
<meta property="og:image" content="https://officer.dev/og-image-v3.jpg" />
|
<meta property="og:image" content="https://officer.dev/og-image-v3.jpg" />
|
||||||
<meta property="og:image:secure_url" content="https://static.officer.dev/og-image-v3.jpg" />
|
<meta property="og:image:secure_url" content="https://officer.dev/og-image-v3.jpg" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<meta property="og:image:width" content="1200" />
|
||||||
<meta property="og:image:height" content="630" />
|
<meta property="og:image:height" content="630" />
|
||||||
<meta property="og:image:type" content="image/jpeg" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
<meta name="twitter:image" content="https://officer.dev/og-image-v3.jpg" />
|
<meta name="twitter:image" content="https://officer.dev/og-image-v3.jpg" />
|
||||||
|
|
||||||
<!-- Favicons & App Icons -->
|
<!-- Favicons & App Icons -->
|
||||||
<link rel="icon" type="image/x-icon" href="https://static.officer.dev/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="https://officer.dev/favicon.ico" />
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="https://static.officer.dev/favicon-96x96.png" />
|
<link rel="icon" type="image/png" sizes="96x96" href="https://officer.dev/favicon-96x96.png" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://static.officer.dev/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="https://officer.dev/apple-touch-icon.png" />
|
||||||
<link rel="manifest" href="https://static.officer.dev/site.webmanifest" />
|
<link rel="manifest" href="https://officer.dev/site.webmanifest" />
|
||||||
<meta name="theme-color" content="#1F2620" />
|
<meta name="theme-color" content="#1F2620" />
|
||||||
|
|
||||||
<script type="module" src="./main.tsx" async></script>
|
<script type="module" src="./main.tsx" async></script>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DuckAvatar } from "./DuckAvatar";
|
import { DuckAvatar } from "./DuckAvatar";
|
||||||
import { PixelGrid } from "@/components/PixelGrid";
|
import { PixelGrid } from "@/components/PixelGrid";
|
||||||
import { useGlobal } from 'hooks/useGlobal';
|
import { useGlobal } from 'hooks/useGlobal';
|
||||||
const landscapebg = 'https://static.officer.dev/landscape1.webp';
|
const landscapebg = '/landscape1.webp';
|
||||||
|
|
||||||
export function Background() {
|
export function Background() {
|
||||||
const [duckHidden] = useGlobal('DUCK_HIDDEN', false);
|
const [duckHidden] = useGlobal('DUCK_HIDDEN', false);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ type DuckModelProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function DuckModel({ onUpdate, onAssetsLoaded, currentSection }: DuckModelProps) {
|
export function DuckModel({ onUpdate, onAssetsLoaded, currentSection }: DuckModelProps) {
|
||||||
const character = useGLTF('https://static.officer.dev/duck3D/Character_output.glb');
|
const character = useGLTF('/duck3D/Character_output.glb');
|
||||||
const animations = useGLTF('https://static.officer.dev/duck3D/Meshy_Merged_Animations.glb');
|
const animations = useGLTF('/duck3D/Meshy_Merged_Animations.glb');
|
||||||
const meshRef = useRef<any>(null);
|
const meshRef = useRef<any>(null);
|
||||||
const { actions } = useAnimations(animations.animations, meshRef);
|
const { actions } = useAnimations(animations.animations, meshRef);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { PixelGrid } from "@/components/PixelGrid";
|
import { PixelGrid } from "@/components/PixelGrid";
|
||||||
const landscapebg = 'https://static.officer.dev/landscape1.webp';
|
const landscapebg = '/landscape1.webp';
|
||||||
|
|
||||||
export function Background() {
|
export function Background() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function Header({ dockItems }: HeaderProps) {
|
|||||||
)}
|
)}
|
||||||
<Link to="/">
|
<Link to="/">
|
||||||
<img
|
<img
|
||||||
src="https://static.officer.dev/officer-logo.svg"
|
src="/officer-logo.svg"
|
||||||
alt="Officer"
|
alt="Officer"
|
||||||
className="h-8 md:h-12 w-auto"
|
className="h-8 md:h-12 w-auto"
|
||||||
style={{ transform: 'skew(-15deg, -2deg)' }}
|
style={{ transform: 'skew(-15deg, -2deg)' }}
|
||||||
|
|||||||
+1
-1
@@ -85,7 +85,7 @@ export const BrowserRelay = () => {
|
|||||||
Download and unzip the extension, then load it in Chrome:
|
Download and unzip the extension, then load it in Chrome:
|
||||||
</p>
|
</p>
|
||||||
<a
|
<a
|
||||||
href="https://static.officer.dev/browser-relay-extension.zip"
|
href="/browser-relay-extension.zip"
|
||||||
download
|
download
|
||||||
className="flex items-center justify-center gap-2 w-full h-11 rounded-md border border-duck-dark/15 dark:border-foreground/15 text-sm font-medium hover:bg-duck-dark/5 dark:hover:bg-foreground/5 transition-colors"
|
className="flex items-center justify-center gap-2 w-full h-11 rounded-md border border-duck-dark/15 dark:border-foreground/15 text-sm font-medium hover:bg-duck-dark/5 dark:hover:bg-foreground/5 transition-colors"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<meta property="og:title" content="Officer Dev (Alpha)" />
|
<meta property="og:title" content="Officer Dev (Alpha)" />
|
||||||
<meta property="og:description" content="Your all-purpose AI operating system. Personal AI assistant, terminal, file browser, code editor, and customizable dashboards — all self-hosted." />
|
<meta property="og:description" content="Your all-purpose AI operating system. Personal AI assistant, terminal, file browser, code editor, and customizable dashboards — all self-hosted." />
|
||||||
<meta property="og:image" content="https://alpha.officer.dev/og-image-v3.jpg" />
|
<meta property="og:image" content="https://alpha.officer.dev/og-image-v3.jpg" />
|
||||||
<meta property="og:image:secure_url" content="https://static.officer.dev/og-image-v3.jpg" />
|
<meta property="og:image:secure_url" content="https://alpha.officer.dev/og-image-v3.jpg" />
|
||||||
<meta property="og:image:width" content="1200" />
|
<meta property="og:image:width" content="1200" />
|
||||||
<meta property="og:image:height" content="630" />
|
<meta property="og:image:height" content="630" />
|
||||||
<meta property="og:image:type" content="image/jpeg" />
|
<meta property="og:image:type" content="image/jpeg" />
|
||||||
@@ -25,10 +25,10 @@
|
|||||||
<meta name="twitter:image" content="https://alpha.officer.dev/og-image-v3.jpg" />
|
<meta name="twitter:image" content="https://alpha.officer.dev/og-image-v3.jpg" />
|
||||||
|
|
||||||
<!-- Favicons & App Icons -->
|
<!-- Favicons & App Icons -->
|
||||||
<link rel="icon" type="image/x-icon" href="https://static.officer.dev/favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="https://alpha.officer.dev/favicon.ico" />
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="https://static.officer.dev/favicon-96x96.png" />
|
<link rel="icon" type="image/png" sizes="96x96" href="https://alpha.officer.dev/favicon-96x96.png" />
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="https://static.officer.dev/apple-touch-icon.png" />
|
<link rel="apple-touch-icon" sizes="180x180" href="https://alpha.officer.dev/apple-touch-icon.png" />
|
||||||
<link rel="manifest" href="https://static.officer.dev/site.webmanifest" />
|
<link rel="manifest" href="https://alpha.officer.dev/site.webmanifest" />
|
||||||
<meta name="theme-color" content="#1F2620" />
|
<meta name="theme-color" content="#1F2620" />
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
|
||||||
|
|||||||
+10
-3
@@ -20,6 +20,13 @@ import { toShellUsername } from './servers/data-path';
|
|||||||
|
|
||||||
const { PORT = '5000' } = process.env;
|
const { PORT = '5000' } = process.env;
|
||||||
|
|
||||||
|
// Build static file routes from public/
|
||||||
|
const publicRoutes: Record<string, (req: Request) => Response> = {};
|
||||||
|
for await (const file of new Bun.Glob('**').scan({ cwd: './public' })) {
|
||||||
|
const bunFile = Bun.file(`./public/${file}`);
|
||||||
|
publicRoutes[`/${file}`] = () => new Response(bunFile);
|
||||||
|
}
|
||||||
|
|
||||||
type WSData = {
|
type WSData = {
|
||||||
userId: number;
|
userId: number;
|
||||||
email: string;
|
email: string;
|
||||||
@@ -256,14 +263,14 @@ const server = serve({
|
|||||||
idleTimeout: 60,
|
idleTimeout: 60,
|
||||||
maxRequestBodySize: 1024 * 1024 * 1024 * 50, // 50 GB
|
maxRequestBodySize: 1024 * 1024 * 1024 * 50, // 50 GB
|
||||||
routes: {
|
routes: {
|
||||||
'/og-image.jpg': () => new Response(Bun.file('public/og-image.jpg'), { headers: { 'Content-Type': 'image/jpeg' } }),
|
...publicRoutes,
|
||||||
'/novnc/*': (req) => {
|
'/novnc/*': (req) => {
|
||||||
const file = Bun.file(`public${new URL(req.url).pathname}`);
|
const file = Bun.file(`public${new URL(req.url).pathname}`);
|
||||||
return new Response(file, { headers: { 'Content-Type': 'application/javascript' } });
|
return new Response(file);
|
||||||
},
|
},
|
||||||
'/vendor/*': (req) => {
|
'/vendor/*': (req) => {
|
||||||
const file = Bun.file(`public${new URL(req.url).pathname}`);
|
const file = Bun.file(`public${new URL(req.url).pathname}`);
|
||||||
return new Response(file, { headers: { 'Content-Type': 'application/javascript' } });
|
return new Response(file);
|
||||||
},
|
},
|
||||||
'/api/dev-server-proxy/*': (req, server) => {
|
'/api/dev-server-proxy/*': (req, server) => {
|
||||||
if (req.headers.get('upgrade') === 'websocket') return upgradeDevServerWs(req, server);
|
if (req.headers.get('upgrade') === 'websocket') return upgradeDevServerWs(req, server);
|
||||||
|
|||||||
@@ -142,13 +142,8 @@ async function getHistoryChanges(
|
|||||||
|
|
||||||
const res = await gmailApiFetch(accessToken, '/history', params);
|
const res = await gmailApiFetch(accessToken, '/history', params);
|
||||||
|
|
||||||
if (res.status === 404) {
|
if (res.status === 404 || res.status === 410) {
|
||||||
// No history — nothing new
|
// historyId too old or invalid — caller should fall back to date-based listing
|
||||||
return { messageIds: [], historyId: latestHistoryId };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.status === 410) {
|
|
||||||
// historyId too old — caller should fall back to date-based listing
|
|
||||||
throw new HistoryExpiredError();
|
throw new HistoryExpiredError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export function OfficerIconLogo({ size = 'xl' }: SvgLogoProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src="https://static.officer.dev/officer-logo.svg"
|
src="/officer-logo.svg"
|
||||||
alt="Officer Icon"
|
alt="Officer Icon"
|
||||||
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function OfficerSvgLogoRegular({ size = 'xl' }: SvgLogoProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src="https://static.officer.dev/officer-logo.svg"
|
src="/officer-logo.svg"
|
||||||
alt="Officer Logo"
|
alt="Officer Logo"
|
||||||
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
||||||
/>
|
/>
|
||||||
@@ -28,7 +28,7 @@ export function OfficerSvgLogoSquare({ size = 'xl' }: SvgLogoProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<img
|
<img
|
||||||
src="https://static.officer.dev/officer-logo.svg"
|
src="/officer-logo.svg"
|
||||||
alt="Officer Logo"
|
alt="Officer Logo"
|
||||||
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
style={{ width: config.width, height: 'auto', transform: 'skew(-15deg, -2deg)' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const Email = ({ invitedBy, url }: EmailProps) => {
|
|||||||
<Container className="rounded-lg bg-white p-8 shadow-lg">
|
<Container className="rounded-lg bg-white p-8 shadow-lg">
|
||||||
<Img
|
<Img
|
||||||
className="mx-auto block"
|
className="mx-auto block"
|
||||||
src="https://static.officer.dev/og-image.jpg"
|
src="/og-image.jpg"
|
||||||
width="480"
|
width="480"
|
||||||
alt="officer.dev"
|
alt="officer.dev"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const MainLayout = ({ children }: { children: ReactNode }) => {
|
|||||||
<Container className="rounded-lg bg-white p-8 shadow-lg">
|
<Container className="rounded-lg bg-white p-8 shadow-lg">
|
||||||
<Img
|
<Img
|
||||||
className="mx-auto block"
|
className="mx-auto block"
|
||||||
src="https://static.officer.dev/officer-logo.png"
|
src="/officer-logo.png"
|
||||||
width="240"
|
width="240"
|
||||||
alt="Officer Logo"
|
alt="Officer Logo"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user