This commit is contained in:
dimitar committed 2026-07-24 04:02:04 +02:00
1 parent 8026a25278
commit 865e3c5255
22 files changed
+1224 -8

No files matched your search

+9
View File
@@ -48,6 +48,15 @@ http {
# Security headers for frontend
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://eu-assets.i.posthog.com; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src 'self' data: https:; font-src 'self' data: https://fonts.gstatic.com; connect-src 'self' https://api.placebo.mk https://cms.placebo.mk https://app.placebo.mk wss://api.placebo.mk https://eu.i.posthog.com https://eu-assets.i.posthog.com; manifest-src 'self';" always;
# Digital Asset Links for Android TWA
location /.well-known/ {
default_type application/json;
add_header Content-Type application/json;
add_header Access-Control-Allow-Origin *;
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# Handle React Router
location / {
try_files $uri $uri/ /index.html;
+104
View File
@@ -0,0 +1,104 @@
# Digital Asset Links Configuration
This file enables Trusted Web Activity (TWA) verification for the Placebo.mk Android app.
## What is this?
Digital Asset Links allows your Android app to open without browser UI by proving you own both the website (`https://app.placebo.mk`) and the Android app (`mk.placebo.app`).
## Current Status
⚠️ **PLACEHOLDER** - The SHA256 fingerprint needs to be updated after PWABuilder generates your signing key.
## How to Update
### Step 1: Generate Android App with PWABuilder
1. Go to https://www.pwabuilder.com/
2. Enter your PWA URL: `https://app.placebo.mk`
3. Click "Package For Stores" → "Android"
4. Download the package
### Step 2: Get SHA256 Fingerprint
PWABuilder will provide the SHA256 fingerprint in the downloaded package. Look for:
- `assetlinks.json` file in the package
- Or in the PWABuilder dashboard under "Android Package Options"
The fingerprint looks like:
```
14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E3:6D:3A:AE:4E:9F:5F:E8:E3:91:A0:7E
```
### Step 3: Update assetlinks.json
Replace `PLACEHOLDER_SHA256_FINGERPRINT_FROM_PWABUILDER` in `assetlinks.json` with your actual fingerprint:
```json
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "mk.placebo.app",
"sha256_cert_fingerprints": [
"14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E3:6D:3A:AE:4E:9F:5F:E8:E3:91:A0:7E"
]
}
}
```
### Step 4: Deploy Updated File
The file must be accessible at:
```
https://app.placebo.mk/.well-known/assetlinks.json
```
Nginx is already configured to serve this file (see `nginx.conf`).
### Step 5: Verify
Use Google's validator tool:
```bash
curl "https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://app.placebo.mk&relation=delegate_permission/common.handle_all_urls"
```
Or visit:
https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://app.placebo.mk
You should see your statement returned with `"debugString": "*** ERRORS parsing assetlinks.json: ..."` until you replace the placeholder.
## Troubleshooting
### File not accessible
- Check nginx configuration includes `.well-known` location
- Verify file permissions (should be readable)
- Test with: `curl https://app.placebo.mk/.well-known/assetlinks.json`
### TWA not opening properly
- Ensure SHA256 fingerprint matches your app's signing certificate
- Verify package name is exactly `mk.placebo.app`
- Check both production and debug fingerprints if testing locally
### Multiple Fingerprints
If you have both debug and release keys, add both:
```json
"sha256_cert_fingerprints": [
"14:6D:E9:... (release key)",
"AA:BB:CC:... (debug key)"
]
```
## Important Notes
- This file must be served with `Content-Type: application/json`
- The file must be accessible via HTTPS (HTTP will fail)
- Changes take effect immediately (no cache)
- Keep your signing key secure - anyone with it can create apps verified for your domain
## Resources
- [Digital Asset Links Documentation](https://developers.google.com/digital-asset-links/v1/getting-started)
- [TWA Guide](https://developers.google.com/web/android/trusted-web-activity)
- [PWABuilder Documentation](https://docs.pwabuilder.com/)
+12
View File
@@ -0,0 +1,12 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "mk.placebo.app",
"sha256_cert_fingerprints": [
"PLACEHOLDER_SHA256_FINGERPRINT_FROM_PWABUILDER"
]
}
}
]
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 987 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

+88
View File
@@ -0,0 +1,88 @@
# Screenshots for Google Play Store
## Required Screenshots
You need to capture **2-8 screenshots** from the deployed PWA at https://app.placebo.mk
### How to Capture Screenshots
#### Using Chrome DevTools (Recommended)
1. Open Chrome browser
2. Navigate to https://app.placebo.mk
3. Press `F12` to open DevTools
4. Click the device toolbar icon (or press `Ctrl+Shift+M`)
5. Select device: **Pixel 5** (1080 x 2340)
6. Capture each screen:
- Click the three dots menu in device toolbar
- Select "Capture screenshot"
- Save as PNG in this directory
#### Recommended Screenshots
Capture these key screens (in order):
1. **screenshot-1-home.png** - Homepage with hero article and latest articles
2. **screenshot-2-article.png** - Article detail page showing full article
3. **screenshot-3-archive.png** - Archive page with article grid
4. **screenshot-4-live-blogs.png** - Live blogs listing page
5. **screenshot-5-live-blog-detail.png** - Live blog detail with real-time updates
6. **screenshot-6-categories.png** - Category page (Sport/Art/Science)
### Google Play Requirements
- **Format**: PNG or JPEG (PNG recommended)
- **Minimum dimensions**: 320px (shortest edge)
- **Maximum dimensions**: 3840px (longest edge)
- **Aspect ratio**: 16:9 or 9:16 recommended
- **File size**: Under 8MB per screenshot
- **Quantity**: Minimum 2, maximum 8
### Current Device Settings
Using **Pixel 5** emulation:
- Resolution: 1080 x 2340 pixels
- Pixel ratio: 2.75
- Portrait orientation
- This meets Google Play requirements perfectly
### After Capturing
Once you've captured the screenshots:
1. Name them as: `screenshot-1.png`, `screenshot-2.png`, etc.
2. Place them in this directory (`pwa/public/screenshots/`)
3. Optionally add them to the PWA manifest for better install UX
4. Upload to Google Play Console when creating the app listing
### Optional: Add to Manifest
If you want screenshots to appear in the browser install prompt, update `vite.config.ts`:
```typescript
manifest: {
// ... other config
screenshots: [
{
src: '/screenshots/screenshot-1.png',
sizes: '1080x2340',
type: 'image/png',
form_factor: 'narrow',
label: 'Homepage with latest articles',
},
// Add more screenshots here
],
}
```
### Tips for Great Screenshots
- ✅ Use actual content (not Lorem Ipsum)
- ✅ Show the app in use with real articles
- ✅ Capture different sections to show variety
- ✅ Make sure text is readable
- ✅ Use light mode (default theme)
- ✅ Clear navigation bar and UI elements
- ❌ Don't include device frames (Google Play adds them)
- ❌ Don't add text overlays (save for marketing images)
- ❌ Don't capture error states or empty screens
@@ -0,0 +1,185 @@
export function PrivacyPolicyComponent() {
return (
<div className="max-w-4xl mx-auto py-8">
<div className="border-brutal-lg bg-card p-8">
<h1 className="font-display text-4xl md:text-5xl mb-6">Политика за приватност</h1>
<div className="font-body space-y-6 text-muted-foreground">
<p className="text-sm text-muted-foreground/70">
Последна промена: {new Date().toLocaleDateString('mk-MK', { year: 'numeric', month: 'long', day: 'numeric' })}
</p>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">1. Вовед</h2>
<p>
Добредојдовте на Placebo.mk ("ние", "наш", "нас"). Ја почитуваме вашата приватност и се посветени на заштитата на вашите лични податоци.
Оваа политика за приватност ви објаснува како собираме, користиме, чуваме и заштитуваме информации кога ја користите нашата апликација.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">2. Информации кои ги собираме</h2>
<h3 className="font-display text-xl text-foreground mb-2 mt-4">2.1 Автоматски собрани информации</h3>
<p>
Кога ја користите нашата апликација, автоматски собираме одредени информации за вашиот уред и користењето на апликацијата:
</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li>Тип на уред и оперативен систем</li>
<li>Верзија на прелистувач</li>
<li>IP адреса</li>
<li>Времиња на пристап и употреба</li>
<li>Страници и содржина која ја прегледувате</li>
<li>Податоци за грешки и перформанси</li>
</ul>
<h3 className="font-display text-xl text-foreground mb-2 mt-4">2.2 Аналитика (PostHog)</h3>
<p>
Користиме PostHog за собирање анонимна аналитика за да го подобриме корисничкото искуство.
PostHog собира информации за тоа како ја користите апликацијата, вклучувајќи:
</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li>Страници што ги посетувате</li>
<li>Времетраење на посета</li>
<li>Интеракции со елементи на апликацијата</li>
<li>Технички информации за вашиот уред</li>
</ul>
<p className="mt-2">
Повеќе информации за практиките за приватност на PostHog можете да најдете на{' '}
<a
href="https://posthog.com/privacy"
target="_blank"
rel="noopener noreferrer"
className="text-accent hover:underline"
>
https://posthog.com/privacy
</a>
</p>
<h3 className="font-display text-xl text-foreground mb-2 mt-4">2.3 Кеширани податоци</h3>
<p>
За да функционира offline, апликацијата кешира содржини (статии, слики) локално на вашиот уред користејќи Service Workers и browser cache.
</p>
<h3 className="font-display text-xl text-foreground mb-2 mt-4">2.4 Кориснички акаунти (доколку се применува)</h3>
<p>
Доколку создадете кориснички акаунт за администраторски функции, собираме:
</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li>Корисничко име</li>
<li>Email адреса</li>
<li>Хеширана лозинка</li>
<li>Улога (администратор/корисник)</li>
</ul>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">3. Како ги користиме вашите податоци</h2>
<p>Вашите податоци ги користиме за:</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li>Да ви ја обезбедиме функционалноста на апликацијата</li>
<li>Да го подобриме корисничкото искуство</li>
<li>Да анализираме како се користи апликацијата</li>
<li>Да ги идентификуваме и поправиме технички проблеми</li>
<li>Да ја заштитиме безбедноста на апликацијата</li>
<li>Да комуницираме со вас за важни промени или ажурирања</li>
</ul>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">4. Споделување на податоци</h2>
<p>
Не ги продаваме, изнајмуваме или трговаме со вашите лични податоци. Податоците може да ги споделиме само во следниве случаи:
</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li><strong>Сервиси провајдери:</strong> PostHog за аналитика (како што е опишано погоре)</li>
<li><strong>Законски барања:</strong> Кога е законски потребно или за заштита на наши права</li>
<li><strong>Деловни трансфери:</strong> Во случај на спојување, аквизиција или продажба на средства</li>
</ul>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">5. Чување на податоци</h2>
<p>
Податоците ги чуваме онолку долго колку што е потребно за целите опишани во оваа политика.
Кеширани податоци се чуваат локално на вашиот уред и може да ги избришете преку поставките на прелистувачот.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">6. Вашите права (GDPR)</h2>
<p>Според GDPR регулативата, имате право на:</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li><strong>Пристап:</strong> Да побарате копија од вашите лични податоци</li>
<li><strong>Корекција:</strong> Да побарате корекција на неточни податоци</li>
<li><strong>Бришење:</strong> Да побарате бришење на вашите податоци ("право на заборав")</li>
<li><strong>Ограничување:</strong> Да побарате ограничување на обработката</li>
<li><strong>Пренос:</strong> Да ги добиете податоците во структуриран, машински читлив формат</li>
<li><strong>Приговор:</strong> Да се спротивставите на обработката на вашите податоци</li>
</ul>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">7. Колачиња (Cookies)</h2>
<p>
Апликацијата користи технологии слични на колачиња (Service Workers, LocalStorage) за:
</p>
<ul className="list-disc list-inside ml-4 space-y-1 mt-2">
<li>Овозможување на offline функционалност</li>
<li>Зачувување на кориснички преференци</li>
<li>Аналитика (преку PostHog)</li>
</ul>
<p className="mt-2">
Можете да ги исклучите преку поставките на вашиот прелистувач, но тоа може да влијае на функционалноста.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">8. Безбедност</h2>
<p>
Превземаме соодветни технички и организациски мерки за заштита на вашите податоци од неовластен пристап,
промена, откривање или уништување. Меѓутоа, ниеден метод на пренос преку интернет или метод на електронско складирање
не е 100% безбеден.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">9. Деца</h2>
<p>
Нашата апликација не е наменета за деца под 13 години. Не собираме свесно лични информации од деца под 13 години.
Доколку сте родител или старател и знаете дека вашето дете ни дало лични информации, ве молиме контактирајте не.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">10. Промени на оваа политика</h2>
<p>
Може периодично да ја ажурираме оваа Политика за приватност. Ќе ве известиме за значајни промени со објавување
на новата политика на оваа страница и ажурирање на "Последна промена" датумот.
</p>
</section>
<section>
<h2 className="font-display text-2xl text-foreground mb-3">11. Контакт</h2>
<p>
Доколку имате прашања за оваа Политика за приватност или сакате да ги користите вашите права,
можете да не контактирате на:
</p>
<div className="mt-2 p-4 border-2 border-foreground bg-background">
<p><strong>Placebo.mk</strong></p>
<p>Email: <a href="mailto:privacy@placebo.mk" className="text-accent hover:underline">privacy@placebo.mk</a></p>
<p>Веб: <a href="https://placebo.mk" className="text-accent hover:underline">https://placebo.mk</a></p>
</div>
</section>
<section className="mt-8 pt-8 border-t-2 border-foreground/20">
<p className="text-sm text-muted-foreground/70">
Со користење на Placebo.mk апликацијата, се согласувате со оваа Политика за приватност.
Доколку не се согласувате со политиката, ве молиме не користете ја апликацијата.
</p>
</section>
</div>
</div>
</div>
)
}
+9
View File
@@ -11,6 +11,7 @@ import { AuthPage } from './components/routes/AuthPage'
import { SportComponent } from './components/routes/SportComponent'
import { ArtComponent } from './components/routes/ArtComponent'
import { ScienceComponent } from './components/routes/ScienceComponent'
import { PrivacyPolicyComponent } from './components/routes/PrivacyPolicyComponent'
import { ProtectedRoute } from './components/auth/ProtectedRoute'
import { Header } from './components/layout/Header'
import { HeroArticle } from './components/home/HeroArticle'
@@ -57,6 +58,7 @@ const rootRoute = createRootRoute({
<li><Link to="/art" className="hover:text-accent transition-colors">Уметност</Link></li>
<li><Link to="/science" className="hover:text-accent transition-colors">Наука</Link></li>
<li><Link to="/archive" className="hover:text-accent transition-colors">Архива</Link></li>
<li><Link to="/privacy-policy" className="hover:text-accent transition-colors">Приватност</Link></li>
</ul>
</div>
<div>
@@ -305,6 +307,12 @@ const adminDashboardRoute = createRoute({
),
})
const privacyPolicyRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/privacy-policy',
component: PrivacyPolicyComponent,
})
const routeTree = rootRoute.addChildren([
indexRoute,
archiveRoute,
@@ -318,6 +326,7 @@ const routeTree = rootRoute.addChildren([
liveBlogAdminRoute,
createLiveBlogRoute,
adminDashboardRoute,
privacyPolicyRoute,
])
export const router = createRouter({ routeTree })
+41 -8
View File
@@ -13,31 +13,64 @@ export default defineConfig({
srcDir: 'src',
filename: 'sw.ts',
registerType: 'autoUpdate',
includeAssets: ['icons/*.svg'],
includeAssets: ['icons/*.png', 'icons/*.svg'],
manifest: {
name: 'Placebo.mk',
short_name: 'Placebo',
description: 'Сатирични вести од Македонија',
lang: 'mk',
start_url: '/',
scope: '/',
display: 'standalone',
orientation: 'portrait-primary',
background_color: '#ffffff',
theme_color: '#000000',
categories: ['news', 'entertainment'],
icons: [
{
src: '/icons/icon-192.svg',
src: '/icons/icon-72.png',
sizes: '72x72',
type: 'image/png',
},
{
src: '/icons/icon-96.png',
sizes: '96x96',
type: 'image/png',
},
{
src: '/icons/icon-128.png',
sizes: '128x128',
type: 'image/png',
},
{
src: '/icons/icon-144.png',
sizes: '144x144',
type: 'image/png',
},
{
src: '/icons/icon-152.png',
sizes: '152x152',
type: 'image/png',
},
{
src: '/icons/icon-192.png',
sizes: '192x192',
type: 'image/svg+xml',
type: 'image/png',
},
{
src: '/icons/icon-512.svg',
sizes: '512x512',
type: 'image/svg+xml',
src: '/icons/icon-384.png',
sizes: '384x384',
type: 'image/png',
},
{
src: '/icons/icon-512.svg',
src: '/icons/icon-512.png',
sizes: '512x512',
type: 'image/svg+xml',
type: 'image/png',
},
{
src: '/icons/icon-512-maskable.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable',
},
],