ridho-arachman-portfolio 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +10 -0
- package/.gitattributes +21 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +80 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +35 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +26 -0
- package/.github/SECURITY.md +43 -0
- package/.github/dependabot.yml +47 -0
- package/.github/workflows/ci.yml +171 -0
- package/.github/workflows/publish.yml +68 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/AGENTS.md +170 -0
- package/CACHING.md +162 -0
- package/DESIGN.md +296 -0
- package/LICENSE +21 -0
- package/README.md +241 -0
- package/commitlint.config.js +3 -0
- package/components.json +25 -0
- package/eslint.config.mjs +18 -0
- package/next.config.ts +146 -0
- package/package.json +112 -0
- package/playwright.config.ts +46 -0
- package/postcss.config.mjs +7 -0
- package/prisma/migrations/20260817181904_add_portfolio_fields/migration.sql +285 -0
- package/prisma/migrations/20260817193303_add_visit_tracking/migration.sql +28 -0
- package/prisma/migrations/20260817205514_add_experience_published/migration.sql +5 -0
- package/prisma/migrations/20260820132310_add_multiple_image/migration.sql +2 -0
- package/prisma/migrations/20260824000000_rename_skill_icon_url_to_icon_name/migration.sql +3 -0
- package/prisma/migrations/20260828144046_change_the_table_name_use_map/migration.sql +204 -0
- package/prisma/migrations/20260926182635_add_site_settings_and_translation/migration.sql +31 -0
- package/prisma/migrations/20260926214617_add_soft_delete/migration.sql +38 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +344 -0
- package/prisma.config.ts +14 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/avatar-hero-sm.webp +0 -0
- package/public/avatar-hero.avif +0 -0
- package/public/avatar-hero.webp +0 -0
- package/public/avatar-nav.webp +0 -0
- package/public/avatar.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/logo-optimized.webp +0 -0
- package/public/logo.png +0 -0
- package/public/manifest.json +42 -0
- package/public/next.svg +1 -0
- package/public/robots.txt +17 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/graphify-execute.bash +3 -0
- package/scripts/seed-admin.ts +81 -0
- package/scripts/seed-portfolio.ts +320 -0
- package/scripts/setup-portfolio-issues.sh +274 -0
- package/scripts/test-integration-setup.mjs +32 -0
- package/src/app/[lang]/(public)/about/about-content.tsx +31 -0
- package/src/app/[lang]/(public)/about/page.tsx +52 -0
- package/src/app/[lang]/(public)/below-fold-sections.tsx +28 -0
- package/src/app/[lang]/(public)/certificates/[slug]/certificate-detail-content.tsx +19 -0
- package/src/app/[lang]/(public)/certificates/[slug]/page.test.ts +38 -0
- package/src/app/[lang]/(public)/certificates/[slug]/page.tsx +89 -0
- package/src/app/[lang]/(public)/certificates/certificates-content.tsx +61 -0
- package/src/app/[lang]/(public)/certificates/page.tsx +58 -0
- package/src/app/[lang]/(public)/contact/contact-content.tsx +14 -0
- package/src/app/[lang]/(public)/contact/page.tsx +33 -0
- package/src/app/[lang]/(public)/experience/[slug]/experience-detail-content.tsx +19 -0
- package/src/app/[lang]/(public)/experience/[slug]/page.test.ts +51 -0
- package/src/app/[lang]/(public)/experience/[slug]/page.tsx +78 -0
- package/src/app/[lang]/(public)/experience/experience-content.tsx +61 -0
- package/src/app/[lang]/(public)/experience/page.tsx +53 -0
- package/src/app/[lang]/(public)/home-content.tsx +111 -0
- package/src/app/[lang]/(public)/layout.tsx +43 -0
- package/src/app/[lang]/(public)/page.tsx +92 -0
- package/src/app/[lang]/(public)/projects/[slug]/page.test.ts +38 -0
- package/src/app/[lang]/(public)/projects/[slug]/page.tsx +91 -0
- package/src/app/[lang]/(public)/projects/[slug]/project-detail-content.tsx +19 -0
- package/src/app/[lang]/(public)/projects/page.tsx +59 -0
- package/src/app/[lang]/(public)/projects/projects-content.tsx +150 -0
- package/src/app/[lang]/layout.tsx +45 -0
- package/src/app/admin/(dashboard)/categories/[id]/edit/page.tsx +21 -0
- package/src/app/admin/(dashboard)/categories/new/page.tsx +16 -0
- package/src/app/admin/(dashboard)/categories/page.tsx +21 -0
- package/src/app/admin/(dashboard)/certificates/[id]/edit/page.tsx +21 -0
- package/src/app/admin/(dashboard)/certificates/new/page.tsx +16 -0
- package/src/app/admin/(dashboard)/certificates/page.tsx +21 -0
- package/src/app/admin/(dashboard)/experience/[id]/edit/page.tsx +21 -0
- package/src/app/admin/(dashboard)/experience/new/page.tsx +16 -0
- package/src/app/admin/(dashboard)/experience/page.tsx +21 -0
- package/src/app/admin/(dashboard)/layout.tsx +40 -0
- package/src/app/admin/(dashboard)/messages/page.tsx +16 -0
- package/src/app/admin/(dashboard)/page.tsx +16 -0
- package/src/app/admin/(dashboard)/projects/[id]/edit/page.tsx +21 -0
- package/src/app/admin/(dashboard)/projects/new/page.tsx +16 -0
- package/src/app/admin/(dashboard)/projects/page-client.tsx +13 -0
- package/src/app/admin/(dashboard)/projects/page.tsx +21 -0
- package/src/app/admin/(dashboard)/settings/page.tsx +16 -0
- package/src/app/admin/(dashboard)/skills/[id]/edit/page.tsx +21 -0
- package/src/app/admin/(dashboard)/skills/new/page.tsx +16 -0
- package/src/app/admin/(dashboard)/skills/page.tsx +21 -0
- package/src/app/admin/(dashboard)/translations/page.tsx +16 -0
- package/src/app/admin/(dashboard)/trash/page.tsx +16 -0
- package/src/app/admin/login/page.tsx +22 -0
- package/src/app/api/admin/analytics/route.ts +124 -0
- package/src/app/api/admin/categories/[id]/purge/route.ts +57 -0
- package/src/app/api/admin/categories/[id]/restore/route.ts +41 -0
- package/src/app/api/admin/categories/[id]/route.ts +113 -0
- package/src/app/api/admin/categories/categories.integration.test.ts +190 -0
- package/src/app/api/admin/categories/route.ts +83 -0
- package/src/app/api/admin/certificates/[id]/purge/route.ts +59 -0
- package/src/app/api/admin/certificates/[id]/restore/route.ts +43 -0
- package/src/app/api/admin/certificates/[id]/route.ts +137 -0
- package/src/app/api/admin/certificates/certificates.integration.test.ts +90 -0
- package/src/app/api/admin/certificates/route.ts +100 -0
- package/src/app/api/admin/dashboard-stats/route.ts +31 -0
- package/src/app/api/admin/experience/[id]/purge/route.ts +59 -0
- package/src/app/api/admin/experience/[id]/restore/route.ts +43 -0
- package/src/app/api/admin/experience/[id]/route.ts +130 -0
- package/src/app/api/admin/experience/experience.integration.test.ts +104 -0
- package/src/app/api/admin/experience/route.ts +93 -0
- package/src/app/api/admin/messages/[id]/purge/route.ts +54 -0
- package/src/app/api/admin/messages/[id]/restore/route.ts +38 -0
- package/src/app/api/admin/messages/[id]/route.ts +92 -0
- package/src/app/api/admin/messages/messages.integration.test.ts +111 -0
- package/src/app/api/admin/messages/route.ts +57 -0
- package/src/app/api/admin/projects/[id]/purge/route.ts +62 -0
- package/src/app/api/admin/projects/[id]/restore/route.ts +45 -0
- package/src/app/api/admin/projects/[id]/route.test.ts +178 -0
- package/src/app/api/admin/projects/[id]/route.ts +154 -0
- package/src/app/api/admin/projects/projects.integration.test.ts +107 -0
- package/src/app/api/admin/projects/route.test.ts +108 -0
- package/src/app/api/admin/projects/route.ts +95 -0
- package/src/app/api/admin/settings/reset/route.ts +83 -0
- package/src/app/api/admin/settings/route.ts +70 -0
- package/src/app/api/admin/settings/settings.integration.test.ts +241 -0
- package/src/app/api/admin/skills/[id]/purge/route.ts +54 -0
- package/src/app/api/admin/skills/[id]/restore/route.ts +38 -0
- package/src/app/api/admin/skills/[id]/route.ts +106 -0
- package/src/app/api/admin/skills/route.ts +79 -0
- package/src/app/api/admin/skills/skills.integration.test.ts +124 -0
- package/src/app/api/admin/translations/route.ts +143 -0
- package/src/app/api/admin/translations/translations.integration.test.ts +153 -0
- package/src/app/api/admin/upload/route.ts +105 -0
- package/src/app/api/auth/[...all]/route.ts +44 -0
- package/src/app/api/contact/route.ts +76 -0
- package/src/app/api/public/categories/route.ts +24 -0
- package/src/app/api/public/certificates/[slug]/route.ts +42 -0
- package/src/app/api/public/certificates/route.ts +54 -0
- package/src/app/api/public/experience/[slug]/route.ts +42 -0
- package/src/app/api/public/experience/route.ts +54 -0
- package/src/app/api/public/projects/[slug]/route.ts +53 -0
- package/src/app/api/public/projects/route.ts +75 -0
- package/src/app/api/public/skills/route.ts +25 -0
- package/src/app/api/visit/route.ts +121 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +620 -0
- package/src/app/layout.test.ts +85 -0
- package/src/app/layout.tsx +143 -0
- package/src/app/not-found.tsx +59 -0
- package/src/app/robots.ts +21 -0
- package/src/app/seo-routes.test.ts +107 -0
- package/src/app/sitemap.ts +70 -0
- package/src/components/layout/footer/constants.ts +49 -0
- package/src/components/layout/footer/footer-bottom.tsx +33 -0
- package/src/components/layout/footer/footer-brand.tsx +33 -0
- package/src/components/layout/footer/footer-links.test.tsx +70 -0
- package/src/components/layout/footer/footer-links.tsx +64 -0
- package/src/components/layout/footer/footer-social.test.tsx +77 -0
- package/src/components/layout/footer/footer-social.tsx +49 -0
- package/src/components/layout/footer/footer.tsx +30 -0
- package/src/components/layout/footer/index.ts +1 -0
- package/src/components/layout/navbar/constants.ts +11 -0
- package/src/components/layout/navbar/cv-download.tsx +125 -0
- package/src/components/layout/navbar/desktop-nav.tsx +171 -0
- package/src/components/layout/navbar/index.ts +1 -0
- package/src/components/layout/navbar/language-switcher.tsx +158 -0
- package/src/components/layout/navbar/nav-collapse-client.test.tsx +119 -0
- package/src/components/layout/navbar/nav-collapse-client.tsx +120 -0
- package/src/components/layout/navbar/navbar-logo.tsx +37 -0
- package/src/components/layout/navbar/navbar.tsx +51 -0
- package/src/components/providers/motion-provider.tsx +13 -0
- package/src/components/providers/nuqs-adapter-loader.tsx +11 -0
- package/src/components/providers/public-content-provider.test.tsx +121 -0
- package/src/components/providers/public-content-provider.tsx +53 -0
- package/src/components/sections/about/about-avatar.tsx +40 -0
- package/src/components/sections/about/about-background.tsx +7 -0
- package/src/components/sections/about/about-content.tsx +43 -0
- package/src/components/sections/about/about.tsx +25 -0
- package/src/components/sections/about/avatar-background.tsx +20 -0
- package/src/components/sections/about/avatar-badge.tsx +25 -0
- package/src/components/sections/about/constants.ts +12 -0
- package/src/components/sections/about/index.ts +1 -0
- package/src/components/sections/about/skills-section.test.tsx +85 -0
- package/src/components/sections/about/skills-section.tsx +63 -0
- package/src/components/sections/about-hero/about-hero-background.tsx +51 -0
- package/src/components/sections/about-hero/about-hero-content.tsx +50 -0
- package/src/components/sections/about-hero/about-hero.tsx +61 -0
- package/src/components/sections/about-hero/constants.ts +19 -0
- package/src/components/sections/about-hero/floating-tech-icon.tsx +18 -0
- package/src/components/sections/about-hero/hero-scroll-progress.test.ts +41 -0
- package/src/components/sections/about-hero/hero-scroll-progress.ts +9 -0
- package/src/components/sections/about-hero/index.ts +1 -0
- package/src/components/sections/about-hero/use-about-hero-animations.ts +61 -0
- package/src/components/sections/admin-categories/categories-list.tsx +194 -0
- package/src/components/sections/admin-categories/category-form-page-wrapper.tsx +26 -0
- package/src/components/sections/admin-categories/category-form-page.tsx +119 -0
- package/src/components/sections/admin-categories/category-form.tsx +213 -0
- package/src/components/sections/admin-categories/constants.ts +38 -0
- package/src/components/sections/admin-categories/index.ts +3 -0
- package/src/components/sections/admin-certificates/certificate-form-page.tsx +110 -0
- package/src/components/sections/admin-certificates/certificate-form.tsx +438 -0
- package/src/components/sections/admin-certificates/certificates-list.tsx +233 -0
- package/src/components/sections/admin-certificates/constants.ts +54 -0
- package/src/components/sections/admin-certificates/index.ts +3 -0
- package/src/components/sections/admin-dashboard/admin-dashboard.tsx +263 -0
- package/src/components/sections/admin-dashboard/admin-mobile-sidebar.tsx +161 -0
- package/src/components/sections/admin-dashboard/admin-sidebar-context.tsx +36 -0
- package/src/components/sections/admin-dashboard/admin-sidebar.tsx +134 -0
- package/src/components/sections/admin-dashboard/admin-topbar-wrapper.tsx +14 -0
- package/src/components/sections/admin-dashboard/admin-topbar.tsx +59 -0
- package/src/components/sections/admin-dashboard/constants.test.ts +64 -0
- package/src/components/sections/admin-dashboard/constants.ts +93 -0
- package/src/components/sections/admin-dashboard/index.ts +7 -0
- package/src/components/sections/admin-dashboard/panel-card.tsx +90 -0
- package/src/components/sections/admin-dashboard/stat-card.tsx +59 -0
- package/src/components/sections/admin-dashboard/visitor-map-leaflet.css +61 -0
- package/src/components/sections/admin-dashboard/visitor-map-leaflet.tsx +249 -0
- package/src/components/sections/admin-dashboard/visitor-map.tsx +246 -0
- package/src/components/sections/admin-dashboard/visits-chart.tsx +120 -0
- package/src/components/sections/admin-experience/constants.ts +81 -0
- package/src/components/sections/admin-experience/experience-form-page.tsx +141 -0
- package/src/components/sections/admin-experience/experience-form.tsx +312 -0
- package/src/components/sections/admin-experience/experiences-list.tsx +222 -0
- package/src/components/sections/admin-experience/index.ts +3 -0
- package/src/components/sections/admin-login/admin-login-background.tsx +32 -0
- package/src/components/sections/admin-login/admin-login-card.tsx +43 -0
- package/src/components/sections/admin-login/admin-login-form.test.tsx +142 -0
- package/src/components/sections/admin-login/admin-login-form.tsx +277 -0
- package/src/components/sections/admin-login/admin-login.tsx +102 -0
- package/src/components/sections/admin-login/constants.ts +26 -0
- package/src/components/sections/admin-login/index.ts +3 -0
- package/src/components/sections/admin-messages/constants.ts +60 -0
- package/src/components/sections/admin-messages/index.ts +1 -0
- package/src/components/sections/admin-messages/messages-inbox.tsx +429 -0
- package/src/components/sections/admin-projects/constants.ts +47 -0
- package/src/components/sections/admin-projects/index.ts +3 -0
- package/src/components/sections/admin-projects/project-form-page.tsx +124 -0
- package/src/components/sections/admin-projects/project-form.tsx +360 -0
- package/src/components/sections/admin-projects/projects-list.test.tsx +110 -0
- package/src/components/sections/admin-projects/projects-list.tsx +215 -0
- package/src/components/sections/admin-settings/constants.ts +71 -0
- package/src/components/sections/admin-settings/danger-zone.test.tsx +129 -0
- package/src/components/sections/admin-settings/danger-zone.tsx +123 -0
- package/src/components/sections/admin-settings/index.ts +1 -0
- package/src/components/sections/admin-settings/password-form.test.tsx +116 -0
- package/src/components/sections/admin-settings/password-form.tsx +130 -0
- package/src/components/sections/admin-settings/profile-form.test.tsx +118 -0
- package/src/components/sections/admin-settings/profile-form.tsx +143 -0
- package/src/components/sections/admin-settings/save-button.tsx +43 -0
- package/src/components/sections/admin-settings/settings-page.tsx +60 -0
- package/src/components/sections/admin-settings/settings-section.tsx +38 -0
- package/src/components/sections/admin-settings/site-form.test.tsx +135 -0
- package/src/components/sections/admin-settings/site-form.tsx +205 -0
- package/src/components/sections/admin-settings/socials-form.test.tsx +94 -0
- package/src/components/sections/admin-settings/socials-form.tsx +123 -0
- package/src/components/sections/admin-skills/constants.test.ts +85 -0
- package/src/components/sections/admin-skills/constants.ts +56 -0
- package/src/components/sections/admin-skills/index.ts +3 -0
- package/src/components/sections/admin-skills/skill-form-page.tsx +106 -0
- package/src/components/sections/admin-skills/skill-form.tsx +234 -0
- package/src/components/sections/admin-skills/skills-list.tsx +214 -0
- package/src/components/sections/admin-translations/constants.ts +21 -0
- package/src/components/sections/admin-translations/index.ts +1 -0
- package/src/components/sections/admin-translations/message-field.tsx +96 -0
- package/src/components/sections/admin-translations/message-tree.ts +95 -0
- package/src/components/sections/admin-translations/translations-editor.test.tsx +197 -0
- package/src/components/sections/admin-translations/translations-editor.tsx +236 -0
- package/src/components/sections/admin-trash/constants.ts +105 -0
- package/src/components/sections/admin-trash/index.ts +10 -0
- package/src/components/sections/admin-trash/trash-list.test.tsx +163 -0
- package/src/components/sections/admin-trash/trash-list.tsx +177 -0
- package/src/components/sections/admin-trash/trash-row.tsx +76 -0
- package/src/components/sections/certificate-detail/certificate-detail-content.tsx +117 -0
- package/src/components/sections/certificate-detail/certificate-detail-hero.tsx +87 -0
- package/src/components/sections/certificate-detail/certificate-detail-navigation.tsx +75 -0
- package/src/components/sections/certificate-detail/certificate-detail.tsx +29 -0
- package/src/components/sections/certificate-detail/constants.ts +4 -0
- package/src/components/sections/certificate-detail/index.ts +4 -0
- package/src/components/sections/certificates/certificate-card.tsx +138 -0
- package/src/components/sections/certificates/certificates-background.tsx +10 -0
- package/src/components/sections/certificates/certificates-section.tsx +80 -0
- package/src/components/sections/certificates/constants.test.ts +78 -0
- package/src/components/sections/certificates/constants.ts +84 -0
- package/src/components/sections/certificates/index.ts +3 -0
- package/src/components/sections/contact/constants.ts +16 -0
- package/src/components/sections/contact/contact-form.test.tsx +167 -0
- package/src/components/sections/contact/contact-form.tsx +181 -0
- package/src/components/sections/contact/contact-info.test.tsx +74 -0
- package/src/components/sections/contact/contact-info.tsx +90 -0
- package/src/components/sections/contact/contact.tsx +44 -0
- package/src/components/sections/contact/index.ts +4 -0
- package/src/components/sections/experience/constants.ts +20 -0
- package/src/components/sections/experience/experience-card.tsx +151 -0
- package/src/components/sections/experience/experience-timeline.tsx +66 -0
- package/src/components/sections/experience/experience.tsx +56 -0
- package/src/components/sections/experience/index.ts +1 -0
- package/src/components/sections/experience-detail/constants.ts +14 -0
- package/src/components/sections/experience-detail/experience-detail-content.tsx +50 -0
- package/src/components/sections/experience-detail/experience-detail-gallery.tsx +70 -0
- package/src/components/sections/experience-detail/experience-detail-hero.tsx +101 -0
- package/src/components/sections/experience-detail/experience-detail-lightbox.tsx +44 -0
- package/src/components/sections/experience-detail/experience-detail-navigation.tsx +87 -0
- package/src/components/sections/experience-detail/experience-detail.tsx +57 -0
- package/src/components/sections/experience-detail/index.ts +1 -0
- package/src/components/sections/experience-detail/use-experience-detail.ts +24 -0
- package/src/components/sections/experience-list/constants.ts +19 -0
- package/src/components/sections/experience-list/experience-list-item.tsx +92 -0
- package/src/components/sections/hero/constants.ts +1 -0
- package/src/components/sections/hero/hero-background.tsx +29 -0
- package/src/components/sections/hero/hero-content.tsx +190 -0
- package/src/components/sections/hero/hero.tsx +16 -0
- package/src/components/sections/hero/index.ts +1 -0
- package/src/components/sections/page-hero/constants.ts +33 -0
- package/src/components/sections/page-hero/index.ts +1 -0
- package/src/components/sections/page-hero/page-hero.test.tsx +58 -0
- package/src/components/sections/page-hero/page-hero.tsx +113 -0
- package/src/components/sections/project-detail/constants.ts +14 -0
- package/src/components/sections/project-detail/index.ts +1 -0
- package/src/components/sections/project-detail/project-detail-content.tsx +101 -0
- package/src/components/sections/project-detail/project-detail-gallery.tsx +100 -0
- package/src/components/sections/project-detail/project-detail-hero.tsx +78 -0
- package/src/components/sections/project-detail/project-detail-lightbox.tsx +51 -0
- package/src/components/sections/project-detail/project-detail-navigation.tsx +124 -0
- package/src/components/sections/project-detail/project-detail.tsx +54 -0
- package/src/components/sections/projects/constants.ts +26 -0
- package/src/components/sections/projects/index.ts +3 -0
- package/src/components/sections/projects/map-project.ts +38 -0
- package/src/components/sections/projects/project-card.tsx +108 -0
- package/src/components/sections/projects/projects-background.tsx +10 -0
- package/src/components/sections/projects/projects.tsx +73 -0
- package/src/components/seo/structured-data.test.tsx +82 -0
- package/src/components/seo/structured-data.tsx +96 -0
- package/src/components/ui/alert-dialog.tsx +187 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/chart.tsx +373 -0
- package/src/components/ui/confirm-action-dialog.test.tsx +81 -0
- package/src/components/ui/confirm-action-dialog.tsx +69 -0
- package/src/components/ui/empty-state.tsx +33 -0
- package/src/components/ui/glass-card.test.tsx +43 -0
- package/src/components/ui/glass-card.tsx +63 -0
- package/src/components/ui/image-upload.tsx +207 -0
- package/src/components/ui/input.tsx +24 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/lazy-section.tsx +49 -0
- package/src/components/ui/magnetic-button.test.tsx +69 -0
- package/src/components/ui/magnetic-button.tsx +102 -0
- package/src/components/ui/multi-image-upload.tsx +281 -0
- package/src/components/ui/not-found-button.tsx +15 -0
- package/src/components/ui/pagination.tsx +110 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +49 -0
- package/src/components/ui/switch.tsx +41 -0
- package/src/components/ui/textarea.tsx +23 -0
- package/src/components/ui/theme-toggle-floating.test.tsx +48 -0
- package/src/components/ui/theme-toggle-floating.tsx +105 -0
- package/src/components/ui/turnstile.tsx +74 -0
- package/src/components/visit-tracker.tsx +43 -0
- package/src/generated/prisma/browser.ts +89 -0
- package/src/generated/prisma/client.ts +113 -0
- package/src/generated/prisma/commonInputTypes.ts +671 -0
- package/src/generated/prisma/enums.ts +49 -0
- package/src/generated/prisma/internal/class.ts +334 -0
- package/src/generated/prisma/internal/prismaNamespace.ts +2162 -0
- package/src/generated/prisma/internal/prismaNamespaceBrowser.ts +359 -0
- package/src/generated/prisma/models/Account.ts +1635 -0
- package/src/generated/prisma/models/Category.ts +1469 -0
- package/src/generated/prisma/models/Certificate.ts +1566 -0
- package/src/generated/prisma/models/Experience.ts +1567 -0
- package/src/generated/prisma/models/Message.ts +1265 -0
- package/src/generated/prisma/models/Project.ts +1880 -0
- package/src/generated/prisma/models/RateLimit.ts +1187 -0
- package/src/generated/prisma/models/Session.ts +1450 -0
- package/src/generated/prisma/models/SiteSettings.ts +1460 -0
- package/src/generated/prisma/models/Skill.ts +1315 -0
- package/src/generated/prisma/models/Translation.ts +1115 -0
- package/src/generated/prisma/models/User.ts +1710 -0
- package/src/generated/prisma/models/Verification.ts +1434 -0
- package/src/generated/prisma/models/Visit.ts +1375 -0
- package/src/generated/prisma/models.ts +25 -0
- package/src/hooks/use-categories.ts +66 -0
- package/src/hooks/use-certificates.ts +66 -0
- package/src/hooks/use-dashboard-stats.ts +25 -0
- package/src/hooks/use-entity-id.test.tsx +89 -0
- package/src/hooks/use-entity-id.ts +20 -0
- package/src/hooks/use-experience.ts +66 -0
- package/src/hooks/use-messages.ts +58 -0
- package/src/hooks/use-pagination.ts +66 -0
- package/src/hooks/use-projects.ts +85 -0
- package/src/hooks/use-settings.ts +53 -0
- package/src/hooks/use-skills.ts +59 -0
- package/src/hooks/use-translation.ts +36 -0
- package/src/hooks/use-translations.ts +54 -0
- package/src/hooks/use-trash.ts +62 -0
- package/src/lib/api-client.ts +56 -0
- package/src/lib/api-helpers.ts +52 -0
- package/src/lib/auth-captcha.ts +35 -0
- package/src/lib/auth-client.ts +14 -0
- package/src/lib/auth-security.integration.test.ts +176 -0
- package/src/lib/auth.integration.test.ts +117 -0
- package/src/lib/auth.ts +98 -0
- package/src/lib/contact.integration.test.ts +99 -0
- package/src/lib/env.ts +38 -0
- package/src/lib/form-schemas.test.ts +252 -0
- package/src/lib/i18n.test.ts +94 -0
- package/src/lib/i18n.ts +52 -0
- package/src/lib/icon-resolver.ts +97 -0
- package/src/lib/lazy-providers.tsx +41 -0
- package/src/lib/prisma-errors.test.ts +79 -0
- package/src/lib/prisma-errors.ts +94 -0
- package/src/lib/prisma.ts +21 -0
- package/src/lib/providers.tsx +6 -0
- package/src/lib/public-content.test.ts +68 -0
- package/src/lib/public-content.ts +43 -0
- package/src/lib/public-providers.tsx +12 -0
- package/src/lib/quick-links.ts +36 -0
- package/src/lib/rate-limit.ts +142 -0
- package/src/lib/security-log.test.ts +41 -0
- package/src/lib/security-log.ts +23 -0
- package/src/lib/session.ts +63 -0
- package/src/lib/settings.test.ts +163 -0
- package/src/lib/settings.ts +144 -0
- package/src/lib/soft-delete.integration.test.ts +497 -0
- package/src/lib/soft-delete.test.ts +56 -0
- package/src/lib/soft-delete.ts +30 -0
- package/src/lib/supabase-storage.ts +160 -0
- package/src/lib/translation-types.ts +4 -0
- package/src/lib/translations.test.ts +255 -0
- package/src/lib/translations.ts +163 -0
- package/src/lib/turnstile.ts +57 -0
- package/src/lib/utils/experience-mapper.ts +73 -0
- package/src/lib/utils/project-filter.test.ts +55 -0
- package/src/lib/utils/project-filter.ts +23 -0
- package/src/lib/utils/skill-group.test.ts +59 -0
- package/src/lib/utils/skill-group.ts +27 -0
- package/src/lib/utils.test.ts +26 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/zod-resolver.test.ts +55 -0
- package/src/lib/zod-resolver.ts +31 -0
- package/src/messages/en.json +326 -0
- package/src/messages/id.json +326 -0
- package/src/providers/theme-provider.test.tsx +118 -0
- package/src/providers/theme-provider.tsx +132 -0
- package/src/proxy.ts +119 -0
- package/src/schema/category.ts +46 -0
- package/src/schema/certificate.ts +81 -0
- package/src/schema/contact.ts +19 -0
- package/src/schema/env.ts +63 -0
- package/src/schema/experience.ts +90 -0
- package/src/schema/login.ts +10 -0
- package/src/schema/message.ts +17 -0
- package/src/schema/project.ts +74 -0
- package/src/schema/settings.ts +116 -0
- package/src/schema/skill.ts +49 -0
- package/src/stores/theme-store.test.ts +30 -0
- package/src/stores/theme-store.ts +18 -0
- package/src/test-fixtures/public-content.ts +29 -0
- package/src/test-fixtures/render.tsx +33 -0
- package/src/types/api.ts +27 -0
- package/src/types/domain.ts +348 -0
- package/src/types/world-atlas.d.ts +24 -0
- package/src/utils/client-ip.test.ts +43 -0
- package/src/utils/client-ip.ts +39 -0
- package/src/utils/slug.test.ts +24 -0
- package/src/utils/slug.ts +11 -0
- package/tsconfig.json +34 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vercel.json +5 -0
- package/vitest.config.ts +36 -0
- package/vitest.integration.config.ts +27 -0
- package/vitest.setup.ts +39 -0
package/.dockerignore
ADDED
package/.gitattributes
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Normalize all text files to LF in the repository and working tree.
|
|
2
|
+
# Required: Docker / nginx / Linux containers break with CRLF.
|
|
3
|
+
* text=auto eol=lf
|
|
4
|
+
|
|
5
|
+
# Explicit binary assets (never normalize)
|
|
6
|
+
*.png binary
|
|
7
|
+
*.jpg binary
|
|
8
|
+
*.jpeg binary
|
|
9
|
+
*.gif binary
|
|
10
|
+
*.ico binary
|
|
11
|
+
*.webp binary
|
|
12
|
+
*.avif binary
|
|
13
|
+
*.woff binary
|
|
14
|
+
*.woff2 binary
|
|
15
|
+
*.ttf binary
|
|
16
|
+
*.eot binary
|
|
17
|
+
*.otf binary
|
|
18
|
+
*.pdf binary
|
|
19
|
+
*.mp4 binary
|
|
20
|
+
*.webm binary
|
|
21
|
+
*.mp3 binary
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @Ridho-arachman
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Bug Report
|
|
2
|
+
description: Report a bug or unexpected behavior
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: description
|
|
7
|
+
attributes:
|
|
8
|
+
label: Description
|
|
9
|
+
description: A clear and concise summary of the bug.
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: steps
|
|
15
|
+
attributes:
|
|
16
|
+
label: Steps to Reproduce
|
|
17
|
+
description: Minimal steps to trigger the bug.
|
|
18
|
+
placeholder: |
|
|
19
|
+
1. Go to '...'
|
|
20
|
+
2. Click on '...'
|
|
21
|
+
3. See error
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
- type: textarea
|
|
26
|
+
id: expected
|
|
27
|
+
attributes:
|
|
28
|
+
label: Expected Behavior
|
|
29
|
+
description: What should have happened instead?
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
|
|
33
|
+
- type: textarea
|
|
34
|
+
id: actual
|
|
35
|
+
attributes:
|
|
36
|
+
label: Actual Behavior
|
|
37
|
+
description: What actually happened?
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
|
|
41
|
+
- type: input
|
|
42
|
+
id: route
|
|
43
|
+
attributes:
|
|
44
|
+
label: Affected Route
|
|
45
|
+
description: Which route shows the bug? Include the locale prefix.
|
|
46
|
+
placeholder: e.g. /en/projects, /id/about, /admin/login
|
|
47
|
+
validations:
|
|
48
|
+
required: false
|
|
49
|
+
|
|
50
|
+
- type: input
|
|
51
|
+
id: os
|
|
52
|
+
attributes:
|
|
53
|
+
label: Operating System
|
|
54
|
+
placeholder: e.g. Windows 11, macOS 15, Ubuntu 24.04
|
|
55
|
+
validations:
|
|
56
|
+
required: false
|
|
57
|
+
|
|
58
|
+
- type: input
|
|
59
|
+
id: browser
|
|
60
|
+
attributes:
|
|
61
|
+
label: Browser
|
|
62
|
+
placeholder: e.g. Chrome 130, Firefox 133, Safari 18
|
|
63
|
+
validations:
|
|
64
|
+
required: false
|
|
65
|
+
|
|
66
|
+
- type: input
|
|
67
|
+
id: node
|
|
68
|
+
attributes:
|
|
69
|
+
label: Node.js Version
|
|
70
|
+
placeholder: e.g. 24.0.0
|
|
71
|
+
validations:
|
|
72
|
+
required: false
|
|
73
|
+
|
|
74
|
+
- type: textarea
|
|
75
|
+
id: screenshots
|
|
76
|
+
attributes:
|
|
77
|
+
label: Screenshots
|
|
78
|
+
description: If applicable, add screenshots to help explain the problem.
|
|
79
|
+
validations:
|
|
80
|
+
required: false
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Feature Request
|
|
2
|
+
description: Suggest a new feature or improvement
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What problem does this feature solve? Is it related to a frustration?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: solution
|
|
15
|
+
attributes:
|
|
16
|
+
label: Proposed Solution
|
|
17
|
+
description: How should this feature work?
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: alternatives
|
|
23
|
+
attributes:
|
|
24
|
+
label: Alternatives Considered
|
|
25
|
+
description: Have you considered any other approaches?
|
|
26
|
+
validations:
|
|
27
|
+
required: false
|
|
28
|
+
|
|
29
|
+
- type: textarea
|
|
30
|
+
id: context
|
|
31
|
+
attributes:
|
|
32
|
+
label: Additional Context
|
|
33
|
+
description: Any other context, mockups, or screenshots.
|
|
34
|
+
validations:
|
|
35
|
+
required: false
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- Briefly describe what this PR does and why. -->
|
|
4
|
+
|
|
5
|
+
## Type of Change
|
|
6
|
+
|
|
7
|
+
- [ ] `feat` โ New feature
|
|
8
|
+
- [ ] `fix` โ Bug fix
|
|
9
|
+
- [ ] `refactor` โ Code refactor (no behavior change)
|
|
10
|
+
- [ ] `chore` โ Tooling, config, or dependency update
|
|
11
|
+
- [ ] `docs` โ Documentation only
|
|
12
|
+
|
|
13
|
+
## Checklist
|
|
14
|
+
|
|
15
|
+
- [ ] `npm run lint` passes (0 errors)
|
|
16
|
+
- [ ] `npx tsc --noEmit` passes
|
|
17
|
+
- [ ] `npm run build` passes (incl. `[lang]` routes EN/ID)
|
|
18
|
+
- [ ] `npm run test:unit` passes
|
|
19
|
+
- [ ] `npm run test:integration` passes (if API/DB touched)
|
|
20
|
+
- [ ] `npm run test:e2e` passes (if user flow touched)
|
|
21
|
+
- [ ] Commit message follows Conventional Commits (`feat:` / `fix:` / ...)
|
|
22
|
+
- [ ] Self-reviewed the diff
|
|
23
|
+
|
|
24
|
+
## Screenshots (if UI change)
|
|
25
|
+
|
|
26
|
+
<!-- Before/after screenshots, or "N/A" -->
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 0.1.x | :white_check_mark: |
|
|
8
|
+
|
|
9
|
+
## Reporting a Vulnerability
|
|
10
|
+
|
|
11
|
+
If you discover a security vulnerability, please report it responsibly.
|
|
12
|
+
|
|
13
|
+
**Do NOT open a public GitHub issue.**
|
|
14
|
+
|
|
15
|
+
Instead, please email: **ridho.arachman22@gmail.com**
|
|
16
|
+
|
|
17
|
+
Include:
|
|
18
|
+
|
|
19
|
+
- Description of the vulnerability
|
|
20
|
+
- Steps to reproduce
|
|
21
|
+
- Potential impact
|
|
22
|
+
|
|
23
|
+
You should receive a response within **48 hours**. We will work with you to understand and address the issue before any public disclosure.
|
|
24
|
+
|
|
25
|
+
## Enforced Controls
|
|
26
|
+
|
|
27
|
+
- Cloudflare Turnstile is fail-closed in production (`TURNSTILE_SECRET_KEY` is required at boot; CI/build uses the official test keys).
|
|
28
|
+
- Test bypasses (`DISABLE_RATE_LIMIT`, empty captcha) are ignored when `NODE_ENV=production`.
|
|
29
|
+
- Admin API requires an `ADMIN` session; destructive actions (purge, settings reset) need explicit confirmation and emit a JSON audit line to stdout.
|
|
30
|
+
- Rate limiting is atomic (DB transaction) and keyed by validated client IP.
|
|
31
|
+
- Public API returns explicit selects only โ no internal columns (`deletedAt`, ordering, timestamps).
|
|
32
|
+
|
|
33
|
+
## Scope
|
|
34
|
+
|
|
35
|
+
This policy applies to vulnerabilities found in:
|
|
36
|
+
|
|
37
|
+
- The portfolio web application
|
|
38
|
+
- API routes and authentication flows
|
|
39
|
+
- Docker and deployment configurations
|
|
40
|
+
|
|
41
|
+
## Disclosure
|
|
42
|
+
|
|
43
|
+
We follow responsible disclosure. Once a fix is released, we will credit the reporter (unless they prefer to remain anonymous).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "npm"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
day: "monday"
|
|
8
|
+
# 0 = Dependabot proposes no version updates. The weekly run kept refilling
|
|
9
|
+
# open-pull-requests-limit slots from a backlog deeper than it looked, so
|
|
10
|
+
# merging never reached an empty queue. To resume updates, change 0 back to
|
|
11
|
+
# a number (5-10) โ the ignore/groups rules below stay ready.
|
|
12
|
+
open-pull-requests-limit: 0
|
|
13
|
+
ignore:
|
|
14
|
+
# eslint-plugin-react has no ESLint 10 release (latest peer stops at ^9.7),
|
|
15
|
+
# so `npm run lint` dies on 'react/display-name' (PR #37). Lift when it
|
|
16
|
+
# adds ^10 to its peer range.
|
|
17
|
+
- dependency-name: "eslint"
|
|
18
|
+
versions: [">=10.0.0"]
|
|
19
|
+
groups:
|
|
20
|
+
react:
|
|
21
|
+
patterns:
|
|
22
|
+
- "react"
|
|
23
|
+
- "react-dom"
|
|
24
|
+
# @vitest/coverage-v8 pins an exact peer on vitest; splitting them makes
|
|
25
|
+
# `npm ci` fail with ERESOLVE (PR #34).
|
|
26
|
+
vitest:
|
|
27
|
+
patterns:
|
|
28
|
+
- "vitest"
|
|
29
|
+
- "@vitest/coverage-v8"
|
|
30
|
+
dev-dependencies:
|
|
31
|
+
dependency-type: "development"
|
|
32
|
+
update-types:
|
|
33
|
+
- "minor"
|
|
34
|
+
- "patch"
|
|
35
|
+
labels:
|
|
36
|
+
- "dependencies"
|
|
37
|
+
- "chore"
|
|
38
|
+
|
|
39
|
+
- package-ecosystem: "github-actions"
|
|
40
|
+
directory: "/"
|
|
41
|
+
schedule:
|
|
42
|
+
interval: "weekly"
|
|
43
|
+
day: "monday"
|
|
44
|
+
open-pull-requests-limit: 0
|
|
45
|
+
labels:
|
|
46
|
+
- "dependencies"
|
|
47
|
+
- "ci"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
name: CI Pipeline
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main", "develop"]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
ci-test:
|
|
16
|
+
name: Lint, Typecheck, Build, Unit & Docker Test
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
|
|
19
|
+
services:
|
|
20
|
+
postgres:
|
|
21
|
+
image: postgres:16-alpine
|
|
22
|
+
env:
|
|
23
|
+
POSTGRES_USER: postgres
|
|
24
|
+
POSTGRES_PASSWORD: postgres
|
|
25
|
+
POSTGRES_DB: portfolio_test
|
|
26
|
+
ports:
|
|
27
|
+
- 5432:5432
|
|
28
|
+
options: >-
|
|
29
|
+
--health-cmd "pg_isready -U postgres -d portfolio_test"
|
|
30
|
+
--health-interval 10s
|
|
31
|
+
--health-timeout 5s
|
|
32
|
+
--health-retries 5
|
|
33
|
+
|
|
34
|
+
steps:
|
|
35
|
+
- name: ๐ Checkout repository
|
|
36
|
+
uses: actions/checkout@v7
|
|
37
|
+
|
|
38
|
+
- name: ๐ข Setup Node.js
|
|
39
|
+
uses: actions/setup-node@v7
|
|
40
|
+
with:
|
|
41
|
+
node-version: 24
|
|
42
|
+
cache: "npm"
|
|
43
|
+
|
|
44
|
+
- name: ๐ฆ Install dependencies
|
|
45
|
+
run: npm ci
|
|
46
|
+
|
|
47
|
+
- name: ๐๏ธ Generate Prisma Client
|
|
48
|
+
run: npx prisma generate
|
|
49
|
+
|
|
50
|
+
- name: ๐ Run Linter
|
|
51
|
+
run: npm run lint
|
|
52
|
+
|
|
53
|
+
- name: ๐ง Type Checking
|
|
54
|
+
run: npx tsc --noEmit
|
|
55
|
+
|
|
56
|
+
- name: ๐งช Unit Tests
|
|
57
|
+
run: npm run test:unit
|
|
58
|
+
|
|
59
|
+
- name: ๐๏ธ Build Next.js App
|
|
60
|
+
env:
|
|
61
|
+
DATABASE_URL: "postgresql://postgres:postgres@localhost:5432/portfolio_test"
|
|
62
|
+
BETTER_AUTH_SECRET: "ci-build-secret-1234567890abcdefghijklmnop"
|
|
63
|
+
BETTER_AUTH_URL: "http://localhost:3000"
|
|
64
|
+
NEXT_PUBLIC_SUPABASE_URL: "https://placeholder.supabase.co"
|
|
65
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: "placeholder-anon-key"
|
|
66
|
+
SUPABASE_SERVICE_ROLE_KEY: "placeholder-service-key"
|
|
67
|
+
# Wajib sejak validasi env fail-closed prod (env.ts): Cloudflare test keys resmi, selalu lolos.
|
|
68
|
+
NEXT_PUBLIC_TURNSTILE_SITE_KEY: "1x00000000000000000000AA"
|
|
69
|
+
TURNSTILE_SECRET_KEY: "1x0000000000000000000000000000000AA"
|
|
70
|
+
run: |
|
|
71
|
+
npx prisma db push
|
|
72
|
+
npm run build
|
|
73
|
+
|
|
74
|
+
- name: ๐ณ Test Docker Build (PENTING!)
|
|
75
|
+
run: |
|
|
76
|
+
echo "Validasi Dockerfile (deps stage only)..."
|
|
77
|
+
docker build -f docker/Dockerfile.prod -t nextjs-app:ci-test --target deps .
|
|
78
|
+
echo "โ
Docker build berhasil! File .dockerignore dan Dockerfile aman."
|
|
79
|
+
|
|
80
|
+
test-integration:
|
|
81
|
+
name: Integration Tests
|
|
82
|
+
runs-on: ubuntu-latest
|
|
83
|
+
|
|
84
|
+
services:
|
|
85
|
+
postgres:
|
|
86
|
+
image: postgres:16-alpine
|
|
87
|
+
env:
|
|
88
|
+
POSTGRES_USER: postgres
|
|
89
|
+
POSTGRES_PASSWORD: postgres
|
|
90
|
+
POSTGRES_DB: portfolio_test
|
|
91
|
+
ports:
|
|
92
|
+
- 5432:5432
|
|
93
|
+
options: >-
|
|
94
|
+
--health-cmd "pg_isready -U postgres -d portfolio_test"
|
|
95
|
+
--health-interval 10s
|
|
96
|
+
--health-timeout 5s
|
|
97
|
+
--health-retries 5
|
|
98
|
+
|
|
99
|
+
env:
|
|
100
|
+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/portfolio_test
|
|
101
|
+
BETTER_AUTH_SECRET: ci-integration-test-secret-1234567890abcdef
|
|
102
|
+
BETTER_AUTH_URL: http://localhost:3000
|
|
103
|
+
|
|
104
|
+
steps:
|
|
105
|
+
- name: ๐ Checkout repository
|
|
106
|
+
uses: actions/checkout@v7
|
|
107
|
+
|
|
108
|
+
- name: ๐ข Setup Node.js
|
|
109
|
+
uses: actions/setup-node@v7
|
|
110
|
+
with:
|
|
111
|
+
node-version: 24
|
|
112
|
+
cache: "npm"
|
|
113
|
+
|
|
114
|
+
- name: ๐ฆ Install dependencies
|
|
115
|
+
run: npm ci
|
|
116
|
+
|
|
117
|
+
- name: ๐๏ธ Generate Prisma Client
|
|
118
|
+
run: npx prisma generate
|
|
119
|
+
|
|
120
|
+
- name: ๐๏ธ Push schema to test database
|
|
121
|
+
run: npx prisma db push
|
|
122
|
+
|
|
123
|
+
- name: ๐งช Integration Tests
|
|
124
|
+
run: npx vitest run --config vitest.integration.config.ts
|
|
125
|
+
|
|
126
|
+
e2e:
|
|
127
|
+
name: End-to-End Tests
|
|
128
|
+
runs-on: ubuntu-latest
|
|
129
|
+
|
|
130
|
+
services:
|
|
131
|
+
postgres:
|
|
132
|
+
image: postgres:16-alpine
|
|
133
|
+
env:
|
|
134
|
+
POSTGRES_USER: postgres
|
|
135
|
+
POSTGRES_PASSWORD: postgres
|
|
136
|
+
POSTGRES_DB: portfolio_test
|
|
137
|
+
ports:
|
|
138
|
+
- 5432:5432
|
|
139
|
+
options: >-
|
|
140
|
+
--health-cmd "pg_isready -U postgres -d portfolio_test"
|
|
141
|
+
--health-interval 10s
|
|
142
|
+
--health-timeout 5s
|
|
143
|
+
--health-retries 5
|
|
144
|
+
|
|
145
|
+
env:
|
|
146
|
+
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/portfolio_test
|
|
147
|
+
|
|
148
|
+
steps:
|
|
149
|
+
- name: ๐ Checkout repository
|
|
150
|
+
uses: actions/checkout@v7
|
|
151
|
+
|
|
152
|
+
- name: ๐ข Setup Node.js
|
|
153
|
+
uses: actions/setup-node@v7
|
|
154
|
+
with:
|
|
155
|
+
node-version: 24
|
|
156
|
+
cache: "npm"
|
|
157
|
+
|
|
158
|
+
- name: ๐ฆ Install dependencies
|
|
159
|
+
run: npm ci
|
|
160
|
+
|
|
161
|
+
- name: ๐๏ธ Push schema to test database
|
|
162
|
+
run: npx prisma db push
|
|
163
|
+
|
|
164
|
+
- name: ๐ฅ๏ธ Install Playwright browsers
|
|
165
|
+
run: npx playwright install --with-deps chromium
|
|
166
|
+
|
|
167
|
+
- name: ๐งฌ Generate Prisma Client
|
|
168
|
+
run: npx prisma generate
|
|
169
|
+
|
|
170
|
+
- name: ๐งช End-to-End Tests
|
|
171
|
+
run: npm run test:e2e
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
# Every push to main publishes a new patch version to npm, so the
|
|
4
|
+
# registry copy is always fresh. Requires repo secret NPM_TOKEN
|
|
5
|
+
# (npmjs.com -> Access Tokens -> publish permission).
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: ["main"]
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
# Serialize publishes; never cancel a publish in flight (avoids
|
|
13
|
+
# duplicate-version races when pushes land close together).
|
|
14
|
+
concurrency:
|
|
15
|
+
group: npm-publish
|
|
16
|
+
cancel-in-progress: false
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: write
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
publish:
|
|
23
|
+
# The version-bump commit this workflow pushes itself must not
|
|
24
|
+
# trigger another publish (infinite loop guard).
|
|
25
|
+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
|
|
26
|
+
name: Bump patch and publish
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- name: ๐ Checkout repository
|
|
31
|
+
uses: actions/checkout@v7
|
|
32
|
+
|
|
33
|
+
- name: ๐ข Setup Node.js with npm registry
|
|
34
|
+
uses: actions/setup-node@v7
|
|
35
|
+
with:
|
|
36
|
+
node-version: 24
|
|
37
|
+
cache: "npm"
|
|
38
|
+
registry-url: "https://registry.npmjs.org"
|
|
39
|
+
|
|
40
|
+
- name: ๐ง Git identity for version commit
|
|
41
|
+
run: |
|
|
42
|
+
git config user.name "github-actions[bot]"
|
|
43
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
44
|
+
|
|
45
|
+
- name: ๐ฆ Install dependencies
|
|
46
|
+
run: npm ci
|
|
47
|
+
|
|
48
|
+
- name: ๐๏ธ Generate Prisma Client (no DB needed)
|
|
49
|
+
run: npx prisma generate
|
|
50
|
+
|
|
51
|
+
- name: ๐ง Typecheck gate (do not publish broken code)
|
|
52
|
+
run: npx tsc --noEmit
|
|
53
|
+
|
|
54
|
+
- name: ๐ข Bump patch version ([skip ci] avoids re-trigger)
|
|
55
|
+
run: |
|
|
56
|
+
npm version patch --no-git-tag-version
|
|
57
|
+
VERSION=$(node -p "require('./package.json').version")
|
|
58
|
+
git add package.json
|
|
59
|
+
git commit -m "chore(release): $VERSION [skip ci]"
|
|
60
|
+
git tag "v$VERSION"
|
|
61
|
+
|
|
62
|
+
- name: ๐ Publish to npm
|
|
63
|
+
run: npm publish --access public
|
|
64
|
+
env:
|
|
65
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
66
|
+
|
|
67
|
+
- name: ๐ท๏ธ Push version commit and tag
|
|
68
|
+
run: git push --follow-tags
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx --no -- commitlint --edit $1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
echo "tunggu!!!"
|