diff --git a/app/components/AppFooter.vue b/app/components/AppFooter.vue index 0018fc414..80565e73d 100644 --- a/app/components/AppFooter.vue +++ b/app/components/AppFooter.vue @@ -18,6 +18,12 @@ const isHome = computed(() => route.name === 'index') {{ $t('footer.about') }} + + {{ $t('privacy_policy.title') }} + + + + +definePageMeta({ + name: 'privacy', +}) + +useSeoMeta({ + title: () => `${$t('privacy_policy.title')} - npmx`, + description: () => $t('privacy_policy.welcome', { app: 'npmx' }), +}) + +defineOgImageComponent('Default', { + title: () => $t('privacy_policy.title'), + description: () => $t('privacy_policy.welcome', { app: 'npmx' }), +}) + +const router = useRouter() +const buildInfo = useAppConfig().buildInfo +const { locale } = useI18n() + + + diff --git a/config/env.ts b/config/env.ts index 049fa6a6a..643b9060d 100644 --- a/config/env.ts +++ b/config/env.ts @@ -73,6 +73,16 @@ export async function getGitInfo() { return { branch, commit, shortCommit } } +export async function getFileLastUpdated(path: string) { + try { + // Get ISO date of last commit for file + const date = await git.log(['-1', '--format=%cI', '--', path]) + return date.latest?.date || new Date().toISOString() + } catch { + return new Date().toISOString() + } +} + export async function getEnv(isDevelopment: boolean) { const { commit, shortCommit, branch } = await getGitInfo() const env = isDevelopment diff --git a/i18n/locales/en.json b/i18n/locales/en.json index 3ee0d579d..9d0f33ac8 100644 --- a/i18n/locales/en.json +++ b/i18n/locales/en.json @@ -971,5 +971,86 @@ "up_to_you": "Up to you!" } } + }, + "privacy_policy": { + "title": "privacy policy", + "last_updated": "Last updated: {date}", + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", + "cookies": { + "title": "Cookies", + "what_are": { + "title": "What are cookies?", + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." + }, + "types": { + "title": "What cookies do we use?", + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", + "bold": "essential technical cookies", + "li1": "{li11}{separator} {li12}", + "li2": "{li21}{separator} {li22}", + "separator": ":", + "cookie_vdpl": "__vdpl", + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", + "cookie_h3": "h3", + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." + }, + "local_storage": { + "title": "Local storage", + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", + "bold": "Local Storage", + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", + "bold2": "contains no personal data nor is it used to track you", + "settings": "settings" + }, + "management": { + "title": "Managing cookies", + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", + "bold": "rejecting essential cookies may prevent full access to the application", + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", + "chrome": "Google Chrome (opens in a new window)", + "firefox": "Mozilla Firefox (opens in a new window)", + "edge": "Microsoft Edge (opens in a new window)" + } + }, + "analytics": { + "title": "Analytics", + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", + "bold": "Vercel Web Analytics", + "p2": "Vercel Analytics is designed with privacy in mind:", + "li1": "It does not use cookies", + "li2": "It does not collect personal identifiers", + "li3": "It does not track users across websites", + "li4": "All data is aggregated and anonymised", + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." + }, + "authenticated": { + "title": "Authenticated users", + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", + "bold": "Atmosphere", + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", + "settings": "settings" + }, + "data_retention": { + "title": "Data retention", + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." + }, + "your_rights": { + "title": "Your rights", + "p1": "You have the right to:", + "li1": "Access information about what data we collect", + "li2": "Clear your local storage and cookies at any time", + "li3": "Disconnect your authenticated session", + "li4": "Request information about our data practices", + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." + }, + "contact": { + "title": "Contact us", + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", + "link": "GitHub repository" + }, + "changes": { + "title": "Changes to this policy", + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." + } } } diff --git a/lunaria/files/en-GB.json b/lunaria/files/en-GB.json index fbd8053be..34f763a9c 100644 --- a/lunaria/files/en-GB.json +++ b/lunaria/files/en-GB.json @@ -971,5 +971,86 @@ "up_to_you": "Up to you!" } } + }, + "privacy_policy": { + "title": "privacy policy", + "last_updated": "Last updated: {date}", + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", + "cookies": { + "title": "Cookies", + "what_are": { + "title": "What are cookies?", + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." + }, + "types": { + "title": "What cookies do we use?", + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", + "bold": "essential technical cookies", + "li1": "{li11}{separator} {li12}", + "li2": "{li21}{separator} {li22}", + "separator": ":", + "cookie_vdpl": "__vdpl", + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", + "cookie_h3": "h3", + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." + }, + "local_storage": { + "title": "Local storage", + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", + "bold": "Local Storage", + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", + "bold2": "contains no personal data nor is it used to track you", + "settings": "settings" + }, + "management": { + "title": "Managing cookies", + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", + "bold": "rejecting essential cookies may prevent full access to the application", + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", + "chrome": "Google Chrome (opens in a new window)", + "firefox": "Mozilla Firefox (opens in a new window)", + "edge": "Microsoft Edge (opens in a new window)" + } + }, + "analytics": { + "title": "Analytics", + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", + "bold": "Vercel Web Analytics", + "p2": "Vercel Analytics is designed with privacy in mind:", + "li1": "It does not use cookies", + "li2": "It does not collect personal identifiers", + "li3": "It does not track users across websites", + "li4": "All data is aggregated and anonymised", + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." + }, + "authenticated": { + "title": "Authenticated users", + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", + "bold": "Atmosphere", + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", + "settings": "settings" + }, + "data_retention": { + "title": "Data retention", + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." + }, + "your_rights": { + "title": "Your rights", + "p1": "You have the right to:", + "li1": "Access information about what data we collect", + "li2": "Clear your local storage and cookies at any time", + "li3": "Disconnect your authenticated session", + "li4": "Request information about our data practices", + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." + }, + "contact": { + "title": "Contact us", + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", + "link": "GitHub repository" + }, + "changes": { + "title": "Changes to this policy", + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." + } } } diff --git a/lunaria/files/en-US.json b/lunaria/files/en-US.json index 3ee0d579d..9d0f33ac8 100644 --- a/lunaria/files/en-US.json +++ b/lunaria/files/en-US.json @@ -971,5 +971,86 @@ "up_to_you": "Up to you!" } } + }, + "privacy_policy": { + "title": "privacy policy", + "last_updated": "Last updated: {date}", + "welcome": "Welcome to {app}. We are committed to protecting your privacy. This policy explains what data we collect, how we use it, and your rights regarding your information.", + "cookies": { + "title": "Cookies", + "what_are": { + "title": "What are cookies?", + "p1": "Cookies are small text files stored on your device when you visit a website. Their purpose is to enhance your browsing experience by remembering certain preferences and settings." + }, + "types": { + "title": "What cookies do we use?", + "p1": "We only use {bold} for purposes strictly necessary for the site's functionality. We do not use third-party or advertising cookies.", + "bold": "essential technical cookies", + "li1": "{li11}{separator} {li12}", + "li2": "{li21}{separator} {li22}", + "separator": ":", + "cookie_vdpl": "__vdpl", + "cookie_vdpl_desc": "This cookie is used by our hosting provider (Vercel) for skew protection. It ensures you fetch assets from the correct deployment version if a new update is released while you are browsing. It does not track you.", + "cookie_h3": "h3", + "cookie_h3_desc": "This is our secure session cookie. It stores the OAuth access token when you connect your Atmosphere account. It is essential for maintaining your authenticated session." + }, + "local_storage": { + "title": "Local storage", + "p1": "In addition to session cookies, we use your browser's {bold} to save your display preferences. This allows us to remember the theme (light/dark) and some other {settings} you have selected, so you don't have to reconfigure them on each visit.", + "bold": "Local Storage", + "p2": "This information is purely functional, stored only on your device, and {bold2}. We use it exclusively to improve your experience on our website.", + "bold2": "contains no personal data nor is it used to track you", + "settings": "settings" + }, + "management": { + "title": "Managing cookies", + "p1": "You can configure your browser to accept, reject, or delete cookies according to your preferences. However, please note that {bold}.", + "bold": "rejecting essential cookies may prevent full access to the application", + "p2": "Below are links with instructions for cookie management in the most commonly used browsers:", + "chrome": "Google Chrome (opens in a new window)", + "firefox": "Mozilla Firefox (opens in a new window)", + "edge": "Microsoft Edge (opens in a new window)" + } + }, + "analytics": { + "title": "Analytics", + "p1": "We use {bold} to understand how visitors use our website. This helps us improve the user experience and identify issues.", + "bold": "Vercel Web Analytics", + "p2": "Vercel Analytics is designed with privacy in mind:", + "li1": "It does not use cookies", + "li2": "It does not collect personal identifiers", + "li3": "It does not track users across websites", + "li4": "All data is aggregated and anonymised", + "p3": "The only information collected includes: page URLs, referrer, country/region, device type, browser, and operating system. This data cannot be used to identify individual users." + }, + "authenticated": { + "title": "Authenticated users", + "p1": "When you connect your {bold} account to npmx, we store your OAuth access token in a secure, HTTP-only session cookie. This token is used solely to authenticate requests on your behalf.", + "bold": "Atmosphere", + "p2": "We do not store your credentials, and we do not access any data beyond what is necessary to provide the features you use. You can disconnect your account at any time from the {settings} page.", + "settings": "settings" + }, + "data_retention": { + "title": "Data retention", + "p1": "Session cookies are automatically deleted when you close your browser or after a period of inactivity. Local storage preferences remain on your device until you clear your browser data. Analytics data is retained in aggregate form and cannot be linked to individual users." + }, + "your_rights": { + "title": "Your rights", + "p1": "You have the right to:", + "li1": "Access information about what data we collect", + "li2": "Clear your local storage and cookies at any time", + "li3": "Disconnect your authenticated session", + "li4": "Request information about our data practices", + "p2": "Since we do not collect personal data, there is typically no personal information to delete or export." + }, + "contact": { + "title": "Contact us", + "p1": "For any questions or concerns about this privacy policy, you can contact us by opening an issue on our {link}.", + "link": "GitHub repository" + }, + "changes": { + "title": "Changes to this policy", + "p1": "We may update this privacy policy from time to time. Any changes will be published on this page with an updated revision date." + } } } diff --git a/modules/build-env.ts b/modules/build-env.ts index 24355dcca..ddf87cd8d 100644 --- a/modules/build-env.ts +++ b/modules/build-env.ts @@ -1,7 +1,7 @@ import type { BuildInfo } from '../shared/types' import { createResolver, defineNuxtModule } from 'nuxt/kit' import { isCI } from 'std-env' -import { getEnv, version } from '../config/env' +import { getEnv, getFileLastUpdated, version } from '../config/env' const { resolve } = createResolver(import.meta.url) @@ -10,7 +10,10 @@ export default defineNuxtModule({ name: 'npmx:build-env', }, async setup(_options, nuxt) { - const { env, commit, shortCommit, branch } = await getEnv(nuxt.options.dev) + const [{ env, commit, shortCommit, branch }, privacyPolicyDate] = await Promise.all([ + getEnv(nuxt.options.dev), + getFileLastUpdated('app/pages/privacy.vue'), + ]) nuxt.options.appConfig = nuxt.options.appConfig || {} nuxt.options.appConfig.env = env @@ -21,6 +24,7 @@ export default defineNuxtModule({ shortCommit, branch, env, + privacyPolicyDate, } satisfies BuildInfo nuxt.options.nitro.publicAssets = nuxt.options.nitro.publicAssets || [] diff --git a/shared/types/env.ts b/shared/types/env.ts index b1583e66d..89bf361cb 100644 --- a/shared/types/env.ts +++ b/shared/types/env.ts @@ -5,4 +5,5 @@ export interface BuildInfo { time: number branch: string env: 'preview' | 'canary' | 'dev' | 'release' + privacyPolicyDate: string }