(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | )
21 | }
22 | )
23 | Input.displayName = "Input"
24 |
25 | export { Input }
26 |
--------------------------------------------------------------------------------
/context/i18n-provider.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import { I18nextProvider } from 'react-i18next'
5 | import i18n from '@/lib/i18next'
6 | import { defaultNS } from '@/i18next.config'
7 |
8 | import { useAppDispatch } from '@/lib/redux/hooks'
9 | import { setAppLanguage } from '@/store/reducers/app-reducer'
10 |
11 | interface I18nProviderProps {
12 | children?: React.ReactNode
13 | value: { language: string }
14 | }
15 |
16 | const I18nProvider = ({ children, value }: I18nProviderProps) => {
17 | const dispatch = useAppDispatch()
18 |
19 | React.useEffect(() => {
20 | i18n.changeLanguage(value?.language)
21 | dispatch(setAppLanguage(value?.language))
22 | }, [value?.language, dispatch])
23 |
24 | return (
25 |
26 | {children}
27 |
28 | )
29 | }
30 |
31 | export { I18nProvider, type I18nProviderProps }
32 |
--------------------------------------------------------------------------------
/lib/jsonwebtoken.ts:
--------------------------------------------------------------------------------
1 | import * as jwt from 'jsonwebtoken'
2 | import {
3 | Secret,
4 | SignOptions,
5 | JwtPayload,
6 | VerifyOptions,
7 | VerifyErrors,
8 | } from 'jsonwebtoken'
9 |
10 | export const secret: Secret = process.env.SECRET_KEY!
11 |
12 | export function jwtSign(
13 | payload: string | object | Buffer,
14 | options?: SignOptions
15 | ): string {
16 | return jwt.sign(payload, secret, { algorithm: 'HS256', ...options })
17 | }
18 |
19 | export type JwtVerify =
20 | | { payload: string | JwtPayload; error: null }
21 | | { payload: null; error: VerifyErrors }
22 |
23 | export function jwtVerify(
24 | token: string,
25 | options?: VerifyOptions & { complete?: false }
26 | ): JwtVerify {
27 | try {
28 | const decoded = jwt.verify(token, secret, options)
29 | return { payload: decoded, error: null }
30 | } catch (e: unknown) {
31 | return { payload: null, error: e as VerifyErrors }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/SV.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/CN.svg:
--------------------------------------------------------------------------------
1 |
2 |
17 |
--------------------------------------------------------------------------------
/app/auth/signup/policy.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import Link from 'next/link'
5 | import { useTrans } from '@/hooks/i18next'
6 |
7 | const Policy = () => {
8 | const { trans } = useTrans()
9 |
10 | return (
11 |
12 | {trans(
13 | 'by_clicking_sign_up_you_agree_to_our_terms_of_service_and_privacy_policy',
14 | {
15 | components: {
16 | link1: (
17 |
21 | ),
22 | link2: (
23 |
27 | ),
28 | },
29 | }
30 | )}
31 |
32 | )
33 | }
34 |
35 | export { Policy }
36 |
--------------------------------------------------------------------------------
/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import {
4 | Toast,
5 | ToastClose,
6 | ToastDescription,
7 | ToastProvider,
8 | ToastTitle,
9 | ToastViewport,
10 | } from "@/components/ui/toast"
11 | import { useToast } from "@/components/ui/use-toast"
12 |
13 | export function Toaster() {
14 | const { toasts } = useToast()
15 |
16 | return (
17 |
18 | {toasts.map(function ({ id, title, description, action, ...props }) {
19 | return (
20 |
21 |
22 | {title && {title}}
23 | {description && (
24 | {description}
25 | )}
26 |
27 | {action}
28 |
29 |
30 | )
31 | })}
32 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/EC.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
--------------------------------------------------------------------------------
/app/dashboard/posts/edit/components/fields/field-title.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import { useTranslation } from 'react-i18next'
5 | import { useFormContext } from 'react-hook-form'
6 |
7 | import {
8 | Form,
9 | FormControl,
10 | FormDescription,
11 | FormField,
12 | FormItem,
13 | FormLabel,
14 | FormMessage,
15 | } from '@/components/ui/form'
16 | import { Input } from '@/components/ui/input'
17 |
18 | const FieldTitle = () => {
19 | const { t } = useTranslation()
20 | const { control } = useFormContext()
21 |
22 | return (
23 | (
27 |
28 |
29 |
30 |
31 |
32 |
33 | )}
34 | />
35 | )
36 | }
37 |
38 | export { FieldTitle }
39 |
--------------------------------------------------------------------------------
/components/footer.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | import { Copyright } from '@/components/copyright'
4 | import { ThemeToggle } from '@/components/theme-toggle'
5 | import { LanguageCombobox } from '@/components/language-combobox'
6 | import { cn } from '@/lib/utils'
7 |
8 | interface FooterProps extends React.HTMLAttributes {}
9 |
10 | const Footer = ({ className, ...props }: FooterProps) => {
11 | return (
12 |
27 | )
28 | }
29 |
30 | export { Footer, type FooterProps }
31 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/ES.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/app/api/v1/email/list/route.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse, type NextRequest } from 'next/server'
2 | import { createClient } from '@/supabase/server'
3 | import { ApiError, revalidates } from '@/lib/utils'
4 | import { authorize } from '@/queries/server/auth'
5 |
6 | export async function GET(request: NextRequest) {
7 | const searchParams = request.nextUrl.searchParams
8 | const userId = searchParams.get('userId') as string
9 |
10 | const { authorized } = await authorize(userId)
11 |
12 | if (!authorized) {
13 | return NextResponse.json(
14 | { data: null, error: new ApiError(401) },
15 | { status: 401 }
16 | )
17 | }
18 |
19 | const supabase = createClient()
20 | const { data: emails, error } = await supabase
21 | .from('emails')
22 | .select('*')
23 | .eq('user_id', userId)
24 |
25 | if (error) {
26 | return NextResponse.json({ data: null, error }, { status: 400 })
27 | }
28 |
29 | return NextResponse.json({ data: emails, error: null })
30 | }
31 |
--------------------------------------------------------------------------------
/app/dashboard/tags/edit/components/metaboxes/metabox-description.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import * as React from 'react'
4 | import { useTranslation } from 'react-i18next'
5 | import { useFormContext } from 'react-hook-form'
6 |
7 | import { FormMessage } from '@/components/ui/form'
8 | import { Textarea } from '@/components/ui/textarea'
9 |
10 | const MetaboxDescription = () => {
11 | const { t } = useTranslation()
12 | const { register, getFieldState, formState } = useFormContext()
13 | const fieldState = getFieldState('description', formState)
14 |
15 | return (
16 |
17 |
{t('description')}
18 |
19 |
24 | {fieldState?.error?.message}
25 |
26 |
27 | )
28 | }
29 |
30 | export { MetaboxDescription }
31 |
--------------------------------------------------------------------------------
/app/dashboard/tags/page.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 |
3 | import { Separator } from '@/components/ui/separator'
4 | import { Title } from '@/components/title'
5 | import { Description } from '@/components/description'
6 |
7 | import { AddTag } from './components/add-tag'
8 | import { TagList } from './tag-list'
9 |
10 | export default function TagsPage() {
11 | return (
12 |
13 |
14 |
15 |
tag_list
16 | create_and_manage_tags
17 |
18 |
19 |
20 | new_tag
21 |
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/NI.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/TF.svg:
--------------------------------------------------------------------------------
1 |
2 |
15 |
--------------------------------------------------------------------------------
/public/data/country-flag-icons/MY.svg:
--------------------------------------------------------------------------------
1 |
2 |
20 |
--------------------------------------------------------------------------------
/app/posts/sitemap.ts:
--------------------------------------------------------------------------------
1 | import { MetadataRoute } from 'next'
2 | import { getPostsAPI } from '@/queries/server/posts'
3 |
4 | export async function generateSitemaps() {
5 | const length = Math.ceil(1 / 10000)
6 |
7 | // Fetch the total number of posts and calculate the number of sitemaps needed
8 | return Array.from({ length }).map((_, i) => ({ id: i + 1 }))
9 | }
10 |
11 | export default async function sitemap({
12 | id,
13 | }: {
14 | id: number
15 | }): Promise {
16 | const routes: MetadataRoute.Sitemap = []
17 |
18 | // Google's limit is 50,000 URLs per sitemap
19 | const { posts } = await getPostsAPI(null, {
20 | page: id,
21 | perPage: 10000,
22 | postType: 'post',
23 | status: 'publish',
24 | })
25 |
26 | if (Array.isArray(posts) && posts?.length > 0) {
27 | for (let i = 0; i < posts.length; i++) {
28 | const { permalink, date } = posts[i]
29 | routes.push({ url: permalink ?? '', lastModified: date ?? '' })
30 | }
31 | }
32 |
33 | return routes
34 | }
35 |
--------------------------------------------------------------------------------
/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Toaster as Sonner } from "sonner"
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = "system" } = useTheme()
10 |
11 | return (
12 |
28 | )
29 | }
30 |
31 | export { Toaster }
32 |
--------------------------------------------------------------------------------
/config/middleware.ts:
--------------------------------------------------------------------------------
1 | // Make sure the new route is registered in the middleware.
2 | // The default registered router routes are as follows:
3 | // ['/', '/auth/:path*', '/dashboard/:path*']
4 | export interface Deny {
5 | source: string
6 | destination: string
7 | authenticated: boolean
8 | }
9 |
10 | export const denies: Deny[] = [
11 | {
12 | source: '/dashboard',
13 | destination: '/auth/signin',
14 | authenticated: false,
15 | },
16 | {
17 | source: '/auth/reset-password',
18 | destination: '/auth/signin',
19 | authenticated: false,
20 | },
21 | {
22 | source: '/auth/blocked',
23 | destination: '/auth/signin',
24 | authenticated: false,
25 | },
26 | {
27 | source: '/api/v1',
28 | destination: '/auth/signin',
29 | authenticated: false,
30 | },
31 | {
32 | source: '/auth/signin',
33 | destination: '/dashboard',
34 | authenticated: true,
35 | },
36 | {
37 | source: '/auth/signup',
38 | destination: '/dashboard',
39 | authenticated: true,
40 | },
41 | ]
42 |
--------------------------------------------------------------------------------