100!
This commit is contained in:
50
docker-compose.local.yml
Normal file
50
docker-compose.local.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: quixotic-postgres
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: quixotic
|
||||
POSTGRES_USER: quixotic
|
||||
POSTGRES_PASSWORD: quixotic123
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
- ./database/init:/docker-entrypoint-initdb.d
|
||||
networks:
|
||||
- quixotic
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U quixotic"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
quixotic-app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
container_name: quixotic-app
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
DATABASE_URL: postgresql://quixotic:quixotic123@postgres:5432/quixotic
|
||||
DATABASE_SSL: false
|
||||
TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:-}
|
||||
WEB_APP_URL: http://localhost:3000
|
||||
volumes:
|
||||
- downloads:/app/downloads
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
networks:
|
||||
- quixotic
|
||||
|
||||
volumes:
|
||||
downloads:
|
||||
postgres-data:
|
||||
|
||||
networks:
|
||||
quixotic:
|
||||
driver: bridge
|
||||
@@ -60,11 +60,12 @@
|
||||
|
||||
<!-- Critical CSS - inline the most important styles -->
|
||||
<style>
|
||||
:root{--tg-color-bg:var(--tg-theme-bg-color,#ffffff);--tg-color-secondary-bg:var(--tg-theme-secondary-bg-color,#f1f1f1);--tg-color-section-bg:var(--tg-theme-section-bg-color,#ffffff);--tg-color-text:var(--tg-theme-text-color,#000000);--tg-color-hint:var(--tg-theme-hint-color,#999999);--tg-color-link:var(--tg-theme-link-color,#007aff);--tg-color-button:var(--tg-theme-button-color,#007aff);--tg-color-button-text:var(--tg-theme-button-text-color,#ffffff);--tg-border-radius:12px;--tg-spacing-lg:16px;--tg-spacing-xl:20px;--tg-spacing-xxl:24px;--tg-font-size-md:16px;--tg-font-size-lg:17px;--tg-font-size-xl:20px;--tg-line-height-normal:1.4;--tg-line-height-relaxed:1.6}*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display','SF Pro Text',system-ui,Helvetica,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{background:var(--tg-color-bg);color:var(--tg-color-text);font-size:var(--tg-font-size-md);line-height:var(--tg-line-height-normal);overflow-x:hidden}.tg-root{min-height:100vh;display:flex;flex-direction:column}.tg-content{flex:1;padding:var(--tg-spacing-lg);padding-bottom:100px;display:flex;flex-direction:column;gap:var(--tg-spacing-xl)}.tg-placeholder{text-align:center;padding:var(--tg-spacing-xxl) var(--tg-spacing-lg);max-width:300px;margin:0 auto}.tg-placeholder__icon{font-size:48px;margin-bottom:var(--tg-spacing-lg);opacity:.6}.tg-placeholder__title{font-size:var(--tg-font-size-xl);font-weight:600;color:var(--tg-color-text);margin-bottom:8px}.tg-placeholder__description{font-size:14px;color:var(--tg-color-hint);line-height:var(--tg-line-height-relaxed)}.tg-hidden{display:none!important}
|
||||
:root{--tg-color-bg:var(--tg-theme-bg-color,#fff);--tg-color-secondary-bg:var(--tg-theme-secondary-bg-color,#f1f1f1);--tg-color-section-bg:var(--tg-theme-section-bg-color,#fff);--tg-color-text:var(--tg-theme-text-color,#000);--tg-color-hint:var(--tg-theme-hint-color,#999);--tg-color-button:var(--tg-theme-button-color,#007aff);--tg-color-button-text:var(--tg-theme-button-text-color,#fff);--tg-border-radius:12px;--tg-spacing-lg:16px;--tg-spacing-xl:20px;--tg-spacing-xxl:24px;--tg-font-size-md:16px;--tg-font-size-lg:17px;--tg-font-size-xl:20px;--tg-line-height-normal:1.4;--tg-line-height-relaxed:1.6}*{margin:0;padding:0;box-sizing:border-box}html,body{height:100%;font-family:-apple-system,BlinkMacSystemFont,'SF Pro Display',system-ui,sans-serif;-webkit-font-smoothing:antialiased}body{background:var(--tg-color-bg);color:var(--tg-color-text);font-size:var(--tg-font-size-md);line-height:var(--tg-line-height-normal);overflow-x:hidden}.tg-root{min-height:100vh;display:flex;flex-direction:column}.tg-content{flex:1;padding:var(--tg-spacing-lg);padding-bottom:100px;display:flex;flex-direction:column;gap:var(--tg-spacing-xl)}.tg-placeholder{text-align:center;padding:var(--tg-spacing-xxl) var(--tg-spacing-lg);max-width:300px;margin:0 auto}.tg-placeholder__icon{font-size:48px;margin-bottom:var(--tg-spacing-lg);opacity:.6}.tg-placeholder__title{font-size:var(--tg-font-size-xl);font-weight:600;margin-bottom:8px}.tg-placeholder__description{font-size:14px;color:var(--tg-color-hint);line-height:var(--tg-line-height-relaxed)}.tg-hidden{display:none!important}.tg-form{position:fixed;bottom:0;left:0;right:0;padding:var(--tg-spacing-lg);background:var(--tg-color-bg);border-top:1px solid var(--tg-color-secondary-bg);z-index:100}.tg-input-wrapper{position:relative}.tg-input{width:100%;height:48px;padding:0 var(--tg-spacing-lg);background:var(--tg-color-section-bg);border:2px solid var(--tg-color-secondary-bg);border-radius:var(--tg-border-radius);font-size:var(--tg-font-size-lg);color:var(--tg-color-text);transition:border-color .2s;outline:0}.tg-input::placeholder{color:var(--tg-color-hint)}.tg-input:focus{border-color:var(--tg-color-button)}
|
||||
</style>
|
||||
|
||||
<!-- Load full CSS asynchronously -->
|
||||
<link rel="stylesheet" href="style.css" media="print" onload="this.media='all'; this.onload=null;">
|
||||
<!-- Load full CSS asynchronously with fallback -->
|
||||
<link rel="preload" href="style.css?v=3" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||
<noscript><link rel="stylesheet" href="style.css?v=3"></noscript>
|
||||
|
||||
<!-- Load Telegram script asynchronously (defer) -->
|
||||
<script src="https://telegram.org/js/telegram-web-app.js" defer></script>
|
||||
@@ -106,6 +107,6 @@
|
||||
</div>
|
||||
|
||||
<!-- Load app script with defer for better performance -->
|
||||
<script src="dist/script.js?v=2" defer></script>
|
||||
<script src="dist/script.js?v=3" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -43,21 +43,21 @@ app.use((req: Request, res: Response, next) => {
|
||||
|
||||
// Optimized caching strategy
|
||||
app.use(express.static('public', {
|
||||
maxAge: '1d', // Cache static assets for 1 day
|
||||
maxAge: '365d', // Cache static assets for 1 year by default
|
||||
etag: true,
|
||||
lastModified: true,
|
||||
setHeaders: (res: Response, filePath: string) => {
|
||||
// Cache images, fonts, etc. longer
|
||||
if (filePath.match(/\.(jpg|jpeg|png|gif|ico|woff|woff2|ttf|eot)$/)) {
|
||||
// Cache images, fonts, etc. with immutable flag
|
||||
if (filePath.match(/\.(jpg|jpeg|png|gif|ico|woff|woff2|ttf|eot|svg)$/)) {
|
||||
res.set('Cache-Control', 'public, max-age=31536000, immutable');
|
||||
}
|
||||
// Cache CSS and JS with version string
|
||||
// Cache CSS and JS with version string for 1 year
|
||||
else if (filePath.match(/\.(css|js)$/)) {
|
||||
res.set('Cache-Control', 'public, max-age=86400'); // 1 day
|
||||
res.set('Cache-Control', 'public, max-age=31536000, immutable'); // 1 year
|
||||
}
|
||||
// HTML files - short cache
|
||||
// HTML files - short cache with revalidation
|
||||
else if (filePath.match(/\.html$/)) {
|
||||
res.set('Cache-Control', 'public, max-age=3600'); // 1 hour
|
||||
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user