Skip to content

Commit 485cd42

Browse files
Merge pull request #315 from DevLoversTeam/develop
Release v0.5.7
2 parents 099076e + 8b22bd8 commit 485cd42

119 files changed

Lines changed: 34440 additions & 1180 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/security.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: Security Check
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
pull_request:
8+
push:
9+
branches: [main, develop]
10+
11+
jobs:
12+
safe-chain:
13+
runs-on: ubuntu-latest
14+
15+
defaults:
16+
run:
17+
working-directory: frontend
18+
19+
steps:
20+
- name: Checkout
21+
uses: actions/checkout@v4
22+
23+
- name: Setup Node
24+
uses: actions/setup-node@v4
25+
with:
26+
node-version: 22
27+
cache: npm
28+
cache-dependency-path: frontend/package-lock.json
29+
30+
- name: Install Safe-chain
31+
run: npm install -g @aikidosec/safe-chain@1.4.2
32+
33+
- name: Setup Safe-chain for CI
34+
run: safe-chain setup-ci
35+
36+
- name: Add Safe-chain to PATH
37+
run: |
38+
echo "$HOME/.safe-chain/bin" >> "$GITHUB_PATH"
39+
echo "$HOME/.safe-chain/shims" >> "$GITHUB_PATH"
40+
41+
- name: Verify Safe-chain is active
42+
run: |
43+
set -euo pipefail
44+
command -v safe-chain
45+
safe-chain --version
46+
NPM_BIN="$(command -v npm)"
47+
echo "npm path: ${NPM_BIN}"
48+
case "${NPM_BIN}" in
49+
*".safe-chain/shims/"*) ;;
50+
*)
51+
echo "Safe-chain npm shim is not active"
52+
exit 1
53+
;;
54+
esac
55+
56+
- name: Install dependencies
57+
run: npm ci --include=dev

CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,3 +457,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
457457

458458
- Fixed inconsistent scroll behavior when navigating Q&A pages
459459
- Improved UX predictability across desktop and mobile devices
460+
461+
## [0.5.7] - 2026-02-12
462+
463+
### Added
464+
465+
- Monitoring & observability:
466+
- Production error and performance monitoring via Sentry
467+
- Global error boundary and release tracking
468+
- Security & CI:
469+
- Safe-chain dependency malware protection in GitHub Actions
470+
- Automated dependency validation on push and pull requests
471+
- SEO & social metadata:
472+
- Full Open Graph and Twitter Card support
473+
- Localized OG metadata and alt text
474+
- Canonical URL handling via metadataBase
475+
- Quiz platform improvements:
476+
- Redis caching for quiz questions to reduce database load
477+
- Guest warning with Login / Sign up / Continue as guest options
478+
- Bot protection: single verification per question attempt
479+
- Shop enhancements:
480+
- Monobank payment integration (UAH-only, feature-gated)
481+
- Secure webhook processing with signature validation and idempotency
482+
- Admin refund and cancel endpoints
483+
- Platform transparency:
484+
- Added `/public/humans.txt` with team, mission, and technology stack
485+
486+
### Changed
487+
488+
- Navigation & UX:
489+
- Added global page transition loading indicators
490+
- Context-aware header behavior for Blog and Shop
491+
- Improved mobile menu interactions and auto-close behavior
492+
- Leaderboard:
493+
- User avatars with DiceBear fallback for missing images
494+
- Home & SEO:
495+
- Home title standardized to "DevLovers"
496+
- Subtitle used for OG/Twitter previews
497+
- Authentication & routing:
498+
- Improved locale detection and dashboard redirect handling
499+
- OAuth and database configuration stability improvements
500+
501+
### Fixed
502+
503+
- Fixed missing locale handling for `/dashboard` redirects
504+
- Restored authentication flow after environment configuration issues
505+
- Improved handling of missing or invalid avatar data
506+
- Fixed OG preview URL resolution issues
507+
- Improved reliability of environment configuration and credentials
508+
509+
### Performance
510+
511+
- Reduced database load for quiz pages via Redis caching
512+
- Improved frontend loading experience during navigation

frontend/.env.example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,4 @@ TRUST_FORWARDED_HEADERS=0
102102
# emergency switch
103103
RATE_LIMIT_DISABLED=0
104104

105-
GROQ_API_KEY=
105+
GROQ_API_KEY=

frontend/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@ next-env.d.ts
4848
CLAUDE.md
4949
_dev-notes/
5050
.claude
51+
52+
# Sentry Config File
53+
.env.sentry-build-plugin

frontend/app/[locale]/dashboard/page.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { getTranslations } from 'next-intl/server';
22

33
import { PostAuthQuizSync } from '@/components/auth/PostAuthQuizSync';
4-
import { QuizResultsSection } from '@/components/dashboard/QuizResultsSection';
54
import { ExplainedTermsCard } from '@/components/dashboard/ExplainedTermsCard';
65
import { ProfileCard } from '@/components/dashboard/ProfileCard';
76
import { QuizSavedBanner } from '@/components/dashboard/QuizSavedBanner';
@@ -111,12 +110,9 @@ export default async function DashboardPage({
111110
<div className="grid gap-8 md:grid-cols-2">
112111
<ProfileCard user={userForDisplay} locale={locale} />
113112
<StatsCard stats={stats} />
114-
</div>
115-
<div className="mt-8">
116-
<ExplainedTermsCard />
117113
</div>
118114
<div className="mt-8">
119-
<QuizResultsSection attempts={lastAttempts} locale={locale} />
115+
<ExplainedTermsCard />
120116
</div>
121117
</main>
122118
</DynamicGridBackground>

frontend/app/[locale]/page.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export async function generateMetadata({
1919
};
2020
const ogLocale = localeMap[locale] ?? 'en_US';
2121

22+
const ogTitle = t('subtitle');
23+
2224
return {
2325
title: t('title'),
2426
description: t('description'),
@@ -31,7 +33,7 @@ export async function generateMetadata({
3133
},
3234
},
3335
openGraph: {
34-
title: t('title'),
36+
title: ogTitle,
3537
description: t('description'),
3638
url: canonicalUrl,
3739
siteName: 'DevLovers',
@@ -48,7 +50,7 @@ export async function generateMetadata({
4850
},
4951
twitter: {
5052
card: 'summary_large_image',
51-
title: t('title'),
53+
title: ogTitle,
5254
description: t('description'),
5355
images: ['/og.png'],
5456
},

frontend/app/[locale]/quiz/[slug]/page.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import type { Metadata } from 'next';
2+
import Image from 'next/image';
23
import { notFound } from 'next/navigation';
34
import { getTranslations } from 'next-intl/server';
4-
import Image from 'next/image';
55

6+
import { QuizContainer } from '@/components/quiz/QuizContainer';
67
import { categoryTabStyles } from '@/data/categoryStyles';
78
import { cn } from '@/lib/utils';
8-
9-
import { QuizContainer } from '@/components/quiz/QuizContainer';
109
import { stripCorrectAnswers } from '@/db/queries/quiz';
1110
import { getQuizBySlug, getQuizQuestionsRandomized } from '@/db/queries/quiz';
1211
import { getCurrentUser } from '@/lib/auth';
@@ -53,10 +52,9 @@ export default async function QuizPage({
5352
notFound();
5453
}
5554

56-
const categoryStyle =
57-
quiz.categorySlug && quiz.categorySlug in categoryTabStyles
58-
? categoryTabStyles[quiz.categorySlug as keyof typeof categoryTabStyles]
59-
: null;
55+
const categoryStyle = quiz.categorySlug
56+
? categoryTabStyles[quiz.categorySlug as keyof typeof categoryTabStyles]
57+
: null;
6058

6159
const parsedSeed = seedParam ? Number.parseInt(seedParam, 10) : Number.NaN;
6260
const seed = Number.isFinite(parsedSeed)
@@ -84,9 +82,15 @@ export default async function QuizPage({
8482
<span className="relative h-8 w-8 shrink-0 sm:h-10 sm:w-10">
8583
<Image
8684
src={categoryStyle.icon}
87-
alt=""
85+
alt={quiz.categoryName ?? quiz.categorySlug ?? 'Category'}
8886
fill
89-
className={cn('object-contain', 'iconClassName' in categoryStyle && categoryStyle.iconClassName)}
87+
sizes="(min-width: 640px) 40px, 32px"
88+
className={cn(
89+
'object-contain',
90+
'iconClassName' in categoryStyle
91+
? categoryStyle.iconClassName
92+
: undefined
93+
)}
9094
/>
9195
</span>
9296
)}

frontend/app/[locale]/shop/orders/[id]/page.tsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,16 @@ export const metadata: Metadata = {
2929
export const dynamic = 'force-dynamic';
3030

3131
type OrderCurrency = (typeof orders.$inferSelect)['currency'];
32+
type OrderPaymentStatus = (typeof orders.$inferSelect)['paymentStatus'];
33+
type OrderPaymentProvider = (typeof orders.$inferSelect)['paymentProvider'];
3234

3335
type OrderDetail = {
3436
id: string;
3537
userId: string | null;
3638
totalAmount: string;
3739
currency: OrderCurrency;
38-
paymentStatus:
39-
| 'pending'
40-
| 'requires_payment'
41-
| 'paid'
42-
| 'failed'
43-
| 'refunded';
44-
paymentProvider: string;
40+
paymentStatus: OrderPaymentStatus;
41+
paymentProvider: OrderPaymentProvider;
4542
paymentIntentId: string | null;
4643
stockRestored: boolean;
4744
restockedAt: string | null;

0 commit comments

Comments
 (0)