Skip to content
Closed
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f1c61e6
[FEAT] Added Language based Cookie
steveCs50 Dec 3, 2024
7560e16
[FEAT] Partially COmpleted
Aspireve Dec 4, 2024
62ccfdb
[CHORE] Added more locales
Aspireve Dec 5, 2024
6dae8b1
[FEAT] Adding more
steveCs50 Dec 5, 2024
ec5e1f0
Merge branch 'main' of https://github.com/gitroomhq/postiz-app
steveCs50 Dec 5, 2024
c67daa9
[FEAt] Added a ton of languages
steveCs50 Dec 9, 2024
20498ec
Merge branch 'main' of https://github.com/Aspireve/postiz-app
Aspireve Dec 9, 2024
778d307
Merge pull request #1 from gitroomhq/main
Aspireve Dec 9, 2024
2b0a5dd
Merge branch 'main' into merge-conflicts
Aspireve Dec 9, 2024
bb3e449
Merge pull request #2 from Aspireve/merge-conflicts
Aspireve Dec 9, 2024
6f25a0a
[FEAT] COmpleted adding internationalisation
steveCs50 Dec 10, 2024
ca3e01c
[BUG] Cleared the typescript error and removed the unused error based…
steveCs50 Dec 10, 2024
ecd5c9a
[IMPROVEMENT] Solved more of the coderabbit errors
steveCs50 Dec 10, 2024
3418b9a
[IMPROVEMENT] Solved more of the coderabbit errors
steveCs50 Dec 10, 2024
b2300c7
Update .eslintrc.json
Aspireve Dec 18, 2024
4194541
Update .eslintrc.json
Aspireve Dec 20, 2024
71cd197
Update users.controller.ts
Aspireve Dec 20, 2024
9e09bd1
Update faq.component.tsx
Aspireve Dec 20, 2024
318a58e
Update editor.tsx
Aspireve Dec 20, 2024
63fa13a
Update launches.component.tsx
Aspireve Dec 20, 2024
a816afb
Update settings.component.tsx
Aspireve Dec 20, 2024
6b4b98d
Update layout.tsx
Aspireve Dec 20, 2024
85917a1
Update title.tsx
Aspireve Dec 20, 2024
f284eb1
Update en.json
Aspireve Dec 20, 2024
d8e4b76
Update fr.json
Aspireve Dec 20, 2024
a8ec4b0
Merge branch 'main' of https://github.com/gitroomhq/postiz-app
Aspireve Dec 28, 2024
d84767e
merge: all changes from Aspireve-> main to postiz->main
Aspireve Dec 28, 2024
feb18b4
chore: modify the package-lock.json to include the next-intl package
Aspireve Dec 28, 2024
da1ff45
chore: solving some more problems
Aspireve Dec 28, 2024
26c238c
chore: solving eslint config error
Aspireve Dec 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {
}
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
Expand All @@ -22,6 +21,12 @@
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nx/javascript"],
"rules": {}
}
},
{
"files": ["**/i18n/**/*.ts", "**/app/layout.ts"],
"rules": {
"@typescript-eslint/no-non-null-assertion": "off"
}
},
]
}
26 changes: 26 additions & 0 deletions apps/backend/src/api/routes/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ import { ApiTags } from '@nestjs/swagger';
import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service';
import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto';
import { HttpForbiddenException } from '@gitroom/nestjs-libraries/services/exception.filter';
import { IsIn } from 'class-validator';


class ChangeLanguageDto {
@IsIn(['en', 'fr'])
language: string;
}

@ApiTags('User')
@Controller('/user')
Expand Down Expand Up @@ -203,4 +210,23 @@ export class UsersController {

response.status(200).send();
}

@Post('/changeLanguage')
async changeLanguage(@Body() { language }: ChangeLanguageDto, @Res({ passthrough: true }) response: Response) {
const maxAge = 1000 * 60 * 60 * 24 * 365 * 1; // 1 years in milliseconds
try {
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
});
Comment on lines +230 to +237
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add path attribute to cookie for enhanced security.

Consider adding the path attribute to the cookie configuration to restrict its scope to the minimum required path:

       response.cookie('NEXT_LOCALE', language, {
         domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
         secure: true,
         httpOnly: true,
         maxAge: maxAge,
         expires:  new Date(Date.now() + maxAge),
         sameSite: 'none',
+        path: '/',  // Restrict cookie to root path or specific path as needed
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
});
response.cookie('NEXT_LOCALE', language, {
domain: getCookieUrlFromDomain(process.env.FRONTEND_URL ?? ''),
secure: true,
httpOnly: true,
maxAge: maxAge,
expires: new Date(Date.now() + maxAge),
sameSite: 'none',
path: '/', // Restrict cookie to root path or specific path as needed
});


response.status(200).send();
} catch (error) {
throw new HttpException('Failed to set language preference', 500);
}
}
}
4 changes: 4 additions & 0 deletions apps/frontend/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { composePlugins, withNx } = require('@nx/next');
const createNextIntlPlugin = require('next-intl/plugin');

const withNextIntl = createNextIntlPlugin();

/**
* @type {import('@nx/next/plugins/with-nx').WithNxOptions}
Expand Down Expand Up @@ -51,6 +54,7 @@ const nextConfig = {
const plugins = [
// Add more Next.js plugins to this list if needed.
withNx,
withNextIntl,
];

module.exports = composePlugins(...plugins)(nextConfig);
4 changes: 3 additions & 1 deletion apps/frontend/src/app/(site)/err/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {Metadata} from "next";
import { useTranslations } from "next-intl";

export const metadata: Metadata = {
title: 'Error',
description: '',
}

export default async function Page() {
const t= useTranslations('Error')
return (
<div>We are experiencing some difficulty, try to refresh the page</div>
<div>{t("errorMsg")}</div>
)
}
56 changes: 30 additions & 26 deletions apps/frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { VariableContextComponent } from '@gitroom/react/helpers/variable.contex
import { Fragment } from 'react';
import { PHProvider } from '@gitroom/react/helpers/posthog';
import UtmSaver from '@gitroom/helpers/utils/utm.saver';
import { NextIntlClientProvider } from 'next-intl';

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'NextIntlClientProvider' is defined but never used.
import { getLocale, getMessages } from 'next-intl/server';
import { ToltScript } from '@gitroom/frontend/components/layout/tolt.script';

const chakra = Chakra_Petch({ weight: '400', subsets: ['latin'] });
Expand All @@ -21,9 +23,10 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
const Plausible = !!process.env.STRIPE_PUBLISHABLE_KEY
? PlausibleProvider
: Fragment;

const locale = await getLocale();
const messages = await getMessages();

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'messages' is assigned a value but never used.
return (
<html className={interClass}>
<html className={interClass} lang={locale}>
<head>
<link
rel="icon"
Expand All @@ -32,32 +35,33 @@ export default async function AppLayout({ children }: { children: ReactNode }) {
/>
</head>
<body className={clsx(chakra.className, 'text-primary dark')}>
<VariableContextComponent
storageProvider={
process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO!}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}
tolt={process.env.NEXT_PUBLIC_TOLT!}
>
<ToltScript />
<Plausible
domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
<NextIntlClientProvider messages={messages}>
{/* @ts-expect-error majorly due to the env variables error */}
<VariableContextComponent
storageProvider={
process.env.STORAGE_PROVIDER as 'local' | 'cloudflare'
}
backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL}
plontoKey={process.env.NEXT_PUBLIC_POLOTNO}
billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY}
discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!}
frontEndUrl={process.env.FRONTEND_URL!}
isGeneral={!!process.env.IS_GENERAL}
uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!}
>
<PHProvider
phkey={process.env.NEXT_PUBLIC_POSTHOG_KEY}
host={process.env.NEXT_PUBLIC_POSTHOG_HOST}
<Plausible
domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'}
>
<UtmSaver />
<LayoutContext>{children}</LayoutContext>
</PHProvider>
</Plausible>
</VariableContextComponent>
<PHProvider
phkey={process.env.NEXT_PUBLIC_POSTHOG_KEY}
host={process.env.NEXT_PUBLIC_POSTHOG_HOST}
>
<UtmSaver />
<LayoutContext>{children}</LayoutContext>
</PHProvider>
</Plausible>
</VariableContextComponent>
</NextIntlClientProvider>
</body>
</html>
);
Expand Down
29 changes: 16 additions & 13 deletions apps/frontend/src/components/billing/faq.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,31 @@ import { FC, useCallback, useState } from 'react';
import clsx from 'clsx';
import interClass from '@gitroom/react/helpers/inter.font';
import { useVariables } from '@gitroom/react/helpers/variable.context';
import { useTranslations } from 'next-intl';

const useFaqList = () => {
const {isGeneral} = useVariables();
const t = useTranslations('Billing')
return [
{
title: `Can I trust ${isGeneral ? 'Postiz' : 'Gitroom'}?`,
description: `${isGeneral ? 'Postiz' : 'Gitroom'} is proudly open-source! We believe in an ethical and transparent culture, meaning that ${isGeneral ? 'Postiz' : 'Gitroom'} will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, <a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">click here</a>.`,
title: t('trust_title', {product: isGeneral ? 'Postiz' : 'Gitroom'}),
description: t('trust_description', {
product: isGeneral ? 'Postiz' : 'Gitroom',
repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
repoLinkEnd: '</a>'
}),
Comment on lines +26 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Security: Ensure HTML in translations is properly sanitized

The use of dangerouslySetInnerHTML with HTML content from translations could potentially lead to XSS vulnerabilities. Consider:

  1. Using a sanitization library
  2. Moving the link component outside of translations

Example safer approach:

-      description: t('trust_description', {
-        product: isGeneral ? 'Postiz' : 'Gitroom',
-        repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
-        repoLinkEnd: '</a>'
-      }),
+      description: (
+        <>
+          {t('trust_description_start', { product: isGeneral ? 'Postiz' : 'Gitroom' })}
+          <a 
+            href="https://github.com/gitroomhq/postiz-app" 
+            target="_blank" 
+            rel="noopener noreferrer" 
+            style={{ textDecoration: 'underline' }}
+          >
+            {t('trust_description_link')}
+          </a>
+          {t('trust_description_end')}
+        </>
+      )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
description: t('trust_description', {
product: isGeneral ? 'Postiz' : 'Gitroom',
repoLink: '<a href="https://github.com/gitroomhq/postiz-app" target="_blank" style="text-decoration: underline;">',
repoLinkEnd: '</a>'
}),
description: (
<>
{t('trust_description_start', { product: isGeneral ? 'Postiz' : 'Gitroom' })}
<a
href="https://github.com/gitroomhq/postiz-app"
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'underline' }}
>
{t('trust_description_link')}
</a>
{t('trust_description_end')}
</>
)

},
{
title: 'What are channels?',
description: `${
isGeneral ? 'Postiz' : 'Gitroom'
} allows you to schedule your posts between different channels.
A channel is a publishing platform where you can schedule your posts.
For example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.`,
title: t('What are channels?'),
description: t('channels_answer?', {product: isGeneral ? 'Postiz' : 'Gitroom'})
Comment on lines +33 to +34
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Standardize translation key format

The translation keys use inconsistent formats:

  • Some use interpolation: channels_answer?
  • Some are full sentences: What are channels?
  • Some are direct strings: What is AI auto-complete?

This makes maintenance harder and could cause issues with translation management.

Standardize to a consistent format, preferably using dot notation and meaningful keys:

-      title: t('What are channels?'),
-      description: t('channels_answer?', {product: isGeneral ? 'Postiz' : 'Gitroom'})
+      title: t('faq.channels.title'),
+      description: t('faq.channels.description', {product: isGeneral ? 'Postiz' : 'Gitroom'})

Also applies to: 37-38, 41-42

},
{
title: 'What are team members?',
description: `If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels`,
title: t('What are team members?'),
description: t(`If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels`),
},
{
title: 'What is AI auto-complete?',
description: `We automate ChatGPT to help you write your social posts and articles`,
title: t('What is AI auto-complete?'),
description: t(`We automate ChatGPT to help you write your social posts and articles`),
},
];
}
Expand Down Expand Up @@ -104,10 +106,11 @@ export const FAQSection: FC<{ title: string; description: string }> = (

export const FAQComponent: FC = () => {
const list = useFaqList();
const t = useTranslations('Billing')
return (
<div>
<h3 className="text-[24px] text-center mt-[81px] mb-[40px]">
Frequently Asked Questions
{t('FrequentlyAskedQuestions')}
</h3>
<div className="gap-[24px] flex-col flex select-none">
{list.map((item, index) => (
Expand Down
16 changes: 9 additions & 7 deletions apps/frontend/src/components/billing/lifetime.deal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useSWRConfig } from 'swr';
import { useToaster } from '@gitroom/react/toaster/toaster';
import { useRouter } from 'next/navigation';
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
import { useTranslations } from 'next-intl';

export const LifetimeDeal = () => {
const fetch = useFetch();
Expand All @@ -19,6 +20,7 @@ export const LifetimeDeal = () => {
const { mutate } = useSWRConfig();
const router = useRouter();
const fireEvents = useFireEvents();
const t= useTranslations("Lifetime")

const claim = useCallback(async () => {
const { success } = await (
Expand All @@ -33,10 +35,10 @@ export const LifetimeDeal = () => {

if (success) {
mutate('/user/self');
toast.show('Successfully claimed the code');
toast.show(t('Successfully claimed the code'));
fireEvents('lifetime_claimed');
} else {
toast.show('Code already claimed or invalid code', 'warning');
toast.show(t('Code already claimed or invalid code'), 'warning');
}

setCode('');
Expand Down Expand Up @@ -115,7 +117,7 @@ export const LifetimeDeal = () => {
<div className="flex gap-[30px]">
<div className="border border-customColor6 bg-sixth p-[24px] flex flex-col gap-[20px] flex-1 rounded-[4px]">
<div className="text-[30px]">
Current Package: {user?.totalChannels > 8 ? 'EXTRA' : user?.tier?.current}
{t("Current Package")}: {user?.totalChannels > 8 ? 'EXTRA' : user?.tier?.current}
</div>

<div className="flex flex-col gap-[10px] justify-center text-[16px] text-customColor18">
Expand Down Expand Up @@ -143,7 +145,7 @@ export const LifetimeDeal = () => {

<div className="border border-customColor6 bg-sixth p-[24px] flex flex-col gap-[20px] flex-1 rounded-[4px]">
<div className="text-[30px]">
Next Package:{' '}
{t("Next Package")}:{' '}
{user?.tier?.current === 'PRO' ? 'EXTRA' : !user?.tier?.current ? 'FREE' : user?.tier?.current === 'STANDARD' ? 'PRO' : 'STANDARD'}
</div>

Expand Down Expand Up @@ -174,8 +176,8 @@ export const LifetimeDeal = () => {
<div className="mt-[20px] flex items-center gap-[10px]">
<div className="flex-1">
<Input
label="Code"
placeholder="Enter your code"
label={t("Code")}
placeholder={t("Enter your code")}
disableForm={true}
name="code"
value={code}
Expand All @@ -184,7 +186,7 @@ export const LifetimeDeal = () => {
</div>
<div>
<Button disabled={code.length < 4} onClick={claim}>
Claim
{t("Claim")}
</Button>
</div>
</div>
Expand Down
Loading