create-carlonicora-app 1.0.0
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/LICENSE +675 -0
- package/README.md +104 -0
- package/bin/cli.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/git.d.ts +7 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +80 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +30 -0
- package/dist/prompts.js.map +1 -0
- package/dist/replacer.d.ts +9 -0
- package/dist/replacer.d.ts.map +1 -0
- package/dist/replacer.js +11 -0
- package/dist/replacer.js.map +1 -0
- package/dist/scaffold.d.ts +3 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +79 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/files.d.ts +6 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +103 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +63 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +52 -0
- package/template/.env.example +159 -0
- package/template/.github/workflows/check-library-updates.yml +71 -0
- package/template/.github/workflows/dev.yml +63 -0
- package/template/.github/workflows/pull-request.yml +55 -0
- package/template/.gitmodules +6 -0
- package/template/.husky/pre-commit +1 -0
- package/template/.husky/pre-push +1 -0
- package/template/.prettierignore +1 -0
- package/template/.prettierrc +13 -0
- package/template/.releaserc +134 -0
- package/template/.vscode/settings.json +16 -0
- package/template/CHANGELOG.md +0 -0
- package/template/CLAUDE.md +34 -0
- package/template/DOCKER.md +1591 -0
- package/template/Dockerfile +228 -0
- package/template/README.md +1 -0
- package/template/apps/api/.prettierrc +12 -0
- package/template/apps/api/eslint.config.mjs +54 -0
- package/template/apps/api/jest.config.js +29 -0
- package/template/apps/api/nest-cli.json +15 -0
- package/template/apps/api/package.json +155 -0
- package/template/apps/api/src/config/config.ts +17 -0
- package/template/apps/api/src/config/enums/job.name.ts +6 -0
- package/template/apps/api/src/config/enums/queue.id.ts +3 -0
- package/template/apps/api/src/config/interfaces/config.interface.ts +3 -0
- package/template/apps/api/src/features/features.modules.ts +6 -0
- package/template/apps/api/src/i18n/en/notifications.json +3 -0
- package/template/apps/api/src/main.ts +23 -0
- package/template/apps/api/src/neo4j.migrations/20250901_001.ts +33 -0
- package/template/apps/api/src/neo4j.migrations/20250901_002.ts +90 -0
- package/template/apps/api/src/neo4j.migrations/20250901_003.ts +57 -0
- package/template/apps/api/src/neo4j.migrations/20250901_004.ts +32 -0
- package/template/apps/api/src/neo4j.migrations/queries/migration.queries.ts +49 -0
- package/template/apps/api/src/types/langchain.d.ts +56 -0
- package/template/apps/api/tsconfig.build.json +4 -0
- package/template/apps/api/tsconfig.json +38 -0
- package/template/apps/web/.swcrc +26 -0
- package/template/apps/web/components.json +21 -0
- package/template/apps/web/eslint.config.mjs +33 -0
- package/template/apps/web/global.d.ts +7 -0
- package/template/apps/web/messages/en.json +249 -0
- package/template/apps/web/next.config.js +50 -0
- package/template/apps/web/package.json +146 -0
- package/template/apps/web/playwright.config.ts +86 -0
- package/template/apps/web/postcss.config.mjs +5 -0
- package/template/apps/web/public/sw.js +32 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(admin)/layout.tsx +49 -0
- package/template/apps/web/src/app/[locale]/(auth)/activation/[code]/page.tsx +13 -0
- package/template/apps/web/src/app/[locale]/(auth)/auth/page.tsx +11 -0
- package/template/apps/web/src/app/[locale]/(auth)/invitation/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(auth)/layout.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(auth)/login/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/logout/page.tsx +5 -0
- package/template/apps/web/src/app/[locale]/(auth)/register/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/reset/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/notifications/page.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/[id]/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/page.tsx +12 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/error.tsx +14 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/loading.tsx +21 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/page.tsx +17 -0
- package/template/apps/web/src/app/[locale]/(main)/error.tsx +62 -0
- package/template/apps/web/src/app/[locale]/(main)/layout.tsx +40 -0
- package/template/apps/web/src/app/[locale]/(main)/page.tsx +41 -0
- package/template/apps/web/src/app/[locale]/layout.tsx +54 -0
- package/template/apps/web/src/app/globals.css +256 -0
- package/template/apps/web/src/config/BootstrapProvider.tsx +13 -0
- package/template/apps/web/src/config/Bootstrapper.ts +77 -0
- package/template/apps/web/src/config/env.ts +51 -0
- package/template/apps/web/src/config/middleware-env.ts +14 -0
- package/template/apps/web/src/enums/feature.ids.ts +3 -0
- package/template/apps/web/src/features/common/components/containers/IndexContainer.tsx +11 -0
- package/template/apps/web/src/features/common/components/details/LayoutDetails.tsx +33 -0
- package/template/apps/web/src/features/common/components/navigations/CommonSidebar.tsx +233 -0
- package/template/apps/web/src/features/common/components/navigations/CreationDropDown.tsx +117 -0
- package/template/apps/web/src/features/common/components/navigations/UserSidebarFooter.tsx +115 -0
- package/template/apps/web/src/features/common/components/navigations/VersionDisplay.tsx +18 -0
- package/template/apps/web/src/features/common/contexts/ErrorContext.tsx +62 -0
- package/template/apps/web/src/i18n/request.ts +13 -0
- package/template/apps/web/src/i18n/routing.ts +9 -0
- package/template/apps/web/src/i18n/useDateFnsLocale.ts +15 -0
- package/template/apps/web/src/proxy.ts +107 -0
- package/template/apps/web/src/server-actions/auth-cookies.ts +134 -0
- package/template/apps/web/src/types/modules.d.ts +10 -0
- package/template/apps/web/src/utils/metadata.ts +50 -0
- package/template/apps/web/src/utils/revalidation.ts +7 -0
- package/template/apps/web/tsconfig.json +51 -0
- package/template/docker-compose.yml +211 -0
- package/template/package.json +72 -0
- package/template/packages/nestjs-neo4jsonapi/.gitkeep +0 -0
- package/template/packages/nextjs-jsonapi/.gitkeep +0 -0
- package/template/packages/shared/package.json +23 -0
- package/template/packages/shared/src/const/roles.id.ts +5 -0
- package/template/packages/shared/src/const/system.roles.id.ts +4 -0
- package/template/packages/shared/src/index.ts +1 -0
- package/template/packages/shared/tsconfig.json +10 -0
- package/template/packages/shared/tsup.config.ts +16 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/tsconfig.base.json +62 -0
- package/template/tsconfig.json +12 -0
- package/template/turbo.json +88 -0
- package/template/versions.production.json +4 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ENV } from "@/config/middleware-env";
|
|
2
|
+
import createMiddleware from "next-intl/middleware";
|
|
3
|
+
import { NextResponse, type NextRequest } from "next/server";
|
|
4
|
+
import { routing } from "./i18n/routing";
|
|
5
|
+
|
|
6
|
+
const intlMiddleware = createMiddleware(routing);
|
|
7
|
+
|
|
8
|
+
async function getNewToken(refreshToken: string): Promise<string | null> {
|
|
9
|
+
try {
|
|
10
|
+
const headers: HeadersInit = {};
|
|
11
|
+
const options: RequestInit = { method: "POST", headers: headers };
|
|
12
|
+
const uri = `${process.env.NEXT_PUBLIC_API_URL}auth/refreshtoken/${refreshToken}`;
|
|
13
|
+
const tokenRefreshResponse = await fetch(uri, options);
|
|
14
|
+
|
|
15
|
+
if (tokenRefreshResponse.ok) {
|
|
16
|
+
const data = await tokenRefreshResponse.json();
|
|
17
|
+
return data.data.attributes.token;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return null;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isTokenCloseToExpiry(token: string): boolean {
|
|
27
|
+
try {
|
|
28
|
+
const base64Url = token.split(".")[1];
|
|
29
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
30
|
+
const payload = JSON.parse(Buffer.from(base64, "base64").toString("utf8"));
|
|
31
|
+
const exp = payload.exp;
|
|
32
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
33
|
+
return exp - currentTime < 300;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default async function proxy(request: NextRequest) {
|
|
40
|
+
const { pathname, searchParams } = request.nextUrl;
|
|
41
|
+
|
|
42
|
+
if (pathname === "/githubconnects/callback") {
|
|
43
|
+
const code = searchParams.get("code");
|
|
44
|
+
const installation_id = searchParams.get("installation_id");
|
|
45
|
+
const setup_action = searchParams.get("setup_action");
|
|
46
|
+
const state = searchParams.get("state");
|
|
47
|
+
|
|
48
|
+
let path = null;
|
|
49
|
+
if (state && state.startsWith("{{name}}")) {
|
|
50
|
+
path = decodeURIComponent(state.substring(5));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const nextPublicAddress = ENV.APP_URL;
|
|
54
|
+
if (state && path && !nextPublicAddress.includes(path)) {
|
|
55
|
+
const redirectUrl = `${path}?code=${encodeURIComponent(code || "")}&installation_id=${encodeURIComponent(installation_id || "")}&setup_action=${encodeURIComponent(setup_action || "")}`;
|
|
56
|
+
return NextResponse.redirect(redirectUrl);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalizedPathname = pathname.replace(/^\/([a-z]{2})(?=\/)/, "");
|
|
61
|
+
|
|
62
|
+
const exemptPaths = ["/", "/activation", "/auth", "/invitation", "/login", "/logout", "/register", "/reset"];
|
|
63
|
+
const isExempt = exemptPaths.some((path) => normalizedPathname.startsWith(path));
|
|
64
|
+
|
|
65
|
+
const refreshToken = request.cookies.get("refreshToken")?.value;
|
|
66
|
+
|
|
67
|
+
if (!isExempt && !refreshToken) {
|
|
68
|
+
return NextResponse.redirect(new URL("/login", request.url));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const requestsSupport = normalizedPathname.startsWith("/support");
|
|
72
|
+
if (
|
|
73
|
+
(!process.env.NEXT_PUBLIC_PRIVATE_INSTALLATION ||
|
|
74
|
+
process.env.NEXT_PUBLIC_PRIVATE_INSTALLATION.toLowerCase() !== "true") &&
|
|
75
|
+
requestsSupport
|
|
76
|
+
) {
|
|
77
|
+
return NextResponse.redirect(new URL("/", request.url));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const intlResponse = intlMiddleware(request);
|
|
81
|
+
const response = intlResponse instanceof NextResponse ? intlResponse : NextResponse.next();
|
|
82
|
+
|
|
83
|
+
const token = request.cookies.get("token")?.value;
|
|
84
|
+
if (refreshToken && (!token || isTokenCloseToExpiry(token))) {
|
|
85
|
+
const newToken = await getNewToken(refreshToken);
|
|
86
|
+
if (newToken) {
|
|
87
|
+
response.cookies.set("token", newToken, {
|
|
88
|
+
httpOnly: false,
|
|
89
|
+
path: "/",
|
|
90
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24),
|
|
91
|
+
});
|
|
92
|
+
response.cookies.set("reloadData", "true", {
|
|
93
|
+
httpOnly: false,
|
|
94
|
+
path: "/",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const fullUrl = request.url.replace(/^http:/, "https:");
|
|
100
|
+
response.headers.set("x-full-url", fullUrl);
|
|
101
|
+
|
|
102
|
+
return response;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const config = {
|
|
106
|
+
matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
|
|
107
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { cookies } from "next/headers";
|
|
4
|
+
import zlib from "zlib";
|
|
5
|
+
|
|
6
|
+
export async function updateToken(params: {
|
|
7
|
+
token?: string;
|
|
8
|
+
refreshToken?: string;
|
|
9
|
+
userId?: string;
|
|
10
|
+
companyId?: string;
|
|
11
|
+
licenseExpirationDate?: Date;
|
|
12
|
+
roles?: string[];
|
|
13
|
+
features?: string[];
|
|
14
|
+
modules?: {
|
|
15
|
+
id: string;
|
|
16
|
+
permissions: {
|
|
17
|
+
create: boolean | string;
|
|
18
|
+
read: boolean | string;
|
|
19
|
+
update: boolean | string;
|
|
20
|
+
delete: boolean | string;
|
|
21
|
+
};
|
|
22
|
+
}[];
|
|
23
|
+
}): Promise<void> {
|
|
24
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
25
|
+
|
|
26
|
+
if (params.token)
|
|
27
|
+
(await cookies()).set({
|
|
28
|
+
name: "token",
|
|
29
|
+
value: params.token,
|
|
30
|
+
httpOnly: false,
|
|
31
|
+
path: "/",
|
|
32
|
+
secure: isProduction,
|
|
33
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24),
|
|
34
|
+
});
|
|
35
|
+
if (params.refreshToken)
|
|
36
|
+
(await cookies()).set({
|
|
37
|
+
name: "refreshToken",
|
|
38
|
+
value: params.refreshToken,
|
|
39
|
+
httpOnly: true,
|
|
40
|
+
path: "/",
|
|
41
|
+
secure: isProduction,
|
|
42
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (params.userId)
|
|
46
|
+
(await cookies()).set({
|
|
47
|
+
name: "userId",
|
|
48
|
+
value: params.userId,
|
|
49
|
+
httpOnly: true,
|
|
50
|
+
path: "/",
|
|
51
|
+
secure: isProduction,
|
|
52
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
if (params.companyId)
|
|
56
|
+
(await cookies()).set({
|
|
57
|
+
name: "companyId",
|
|
58
|
+
value: params.companyId,
|
|
59
|
+
httpOnly: true,
|
|
60
|
+
path: "/",
|
|
61
|
+
secure: isProduction,
|
|
62
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (params.licenseExpirationDate)
|
|
66
|
+
(await cookies()).set({
|
|
67
|
+
name: "licenseExpirationDate",
|
|
68
|
+
value: params.licenseExpirationDate.toISOString(),
|
|
69
|
+
httpOnly: true,
|
|
70
|
+
path: "/",
|
|
71
|
+
secure: isProduction,
|
|
72
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (params.roles)
|
|
76
|
+
(await cookies()).set({
|
|
77
|
+
name: "roles",
|
|
78
|
+
value: JSON.stringify(params.roles),
|
|
79
|
+
httpOnly: true,
|
|
80
|
+
path: "/",
|
|
81
|
+
secure: isProduction,
|
|
82
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (params.features)
|
|
86
|
+
(await cookies()).set({
|
|
87
|
+
name: "features",
|
|
88
|
+
value: JSON.stringify(params.features),
|
|
89
|
+
httpOnly: true,
|
|
90
|
+
path: "/",
|
|
91
|
+
secure: isProduction,
|
|
92
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (params.modules) {
|
|
96
|
+
const compressedValue = zlib.gzipSync(JSON.stringify(params.modules)).toString("base64");
|
|
97
|
+
|
|
98
|
+
(await cookies()).set({
|
|
99
|
+
name: "modules",
|
|
100
|
+
value: JSON.stringify(compressedValue),
|
|
101
|
+
httpOnly: true,
|
|
102
|
+
path: "/",
|
|
103
|
+
secure: isProduction,
|
|
104
|
+
expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function removeToken(): Promise<void> {
|
|
110
|
+
(await cookies()).delete({
|
|
111
|
+
name: "token",
|
|
112
|
+
});
|
|
113
|
+
(await cookies()).delete({
|
|
114
|
+
name: "refreshToken",
|
|
115
|
+
});
|
|
116
|
+
(await cookies()).delete({
|
|
117
|
+
name: "userId",
|
|
118
|
+
});
|
|
119
|
+
(await cookies()).delete({
|
|
120
|
+
name: "companyId",
|
|
121
|
+
});
|
|
122
|
+
(await cookies()).delete({
|
|
123
|
+
name: "licenseExpirationDate",
|
|
124
|
+
});
|
|
125
|
+
(await cookies()).delete({
|
|
126
|
+
name: "roles",
|
|
127
|
+
});
|
|
128
|
+
(await cookies()).delete({
|
|
129
|
+
name: "features",
|
|
130
|
+
});
|
|
131
|
+
(await cookies()).delete({
|
|
132
|
+
name: "modules",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AllModuleDefinitions } from "@/config/Bootstrapper";
|
|
2
|
+
import type { FoundationModuleDefinitions } from "@carlonicora/nextjs-jsonapi/core";
|
|
3
|
+
|
|
4
|
+
// Simply reference the type from Bootstrapper - NO MANUAL LIST NEEDED
|
|
5
|
+
// This augments AppModuleDefinitions with feature modules only
|
|
6
|
+
// (foundation types already defined in library's FoundationModuleDefinitions)
|
|
7
|
+
declare module "@carlonicora/nextjs-jsonapi/core" {
|
|
8
|
+
// Override the combined type to use our full definition
|
|
9
|
+
interface AppModuleDefinitions extends Omit<AllModuleDefinitions, keyof FoundationModuleDefinitions> {}
|
|
10
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ENV } from "@/config/env";
|
|
2
|
+
import { Metadata } from "next";
|
|
3
|
+
import { getTranslations } from "next-intl/server";
|
|
4
|
+
import { headers } from "next/headers";
|
|
5
|
+
|
|
6
|
+
//https://nextjs.org/docs/app/api-reference/functions/generate-metadata
|
|
7
|
+
|
|
8
|
+
export async function generateSpecificMetadata(params: {
|
|
9
|
+
title?: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
url?: string;
|
|
12
|
+
}): Promise<Metadata> {
|
|
13
|
+
const t = await getTranslations();
|
|
14
|
+
|
|
15
|
+
const url = (await headers()).get("x-full-url") ?? ENV.APP_URL ?? "{{name}}.com";
|
|
16
|
+
|
|
17
|
+
const title: string = params.title ? `${params.title} | ${t(`generic.title`)}` : t(`generic.title`);
|
|
18
|
+
const description = params.description ? params.description : t(`generic.description`);
|
|
19
|
+
|
|
20
|
+
const response: Metadata = {
|
|
21
|
+
title: title,
|
|
22
|
+
description: description,
|
|
23
|
+
keywords: [],
|
|
24
|
+
publisher: "Phlow",
|
|
25
|
+
openGraph: {
|
|
26
|
+
type: "website",
|
|
27
|
+
title: title,
|
|
28
|
+
description: description,
|
|
29
|
+
url: url,
|
|
30
|
+
siteName: "Phlow",
|
|
31
|
+
},
|
|
32
|
+
twitter: {
|
|
33
|
+
card: "summary_large_image",
|
|
34
|
+
title: title,
|
|
35
|
+
description: description,
|
|
36
|
+
},
|
|
37
|
+
metadataBase: new URL(ENV.APP_URL ?? "{{name}}.com"),
|
|
38
|
+
alternates: {
|
|
39
|
+
canonical: url,
|
|
40
|
+
languages: {
|
|
41
|
+
en: "/en",
|
|
42
|
+
it: "/it",
|
|
43
|
+
fr: "/fr",
|
|
44
|
+
fi: "/fi",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"composite": true,
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"baseUrl": ".",
|
|
6
|
+
"target": "esnext",
|
|
7
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
8
|
+
"allowJs": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"strict": false,
|
|
11
|
+
"strictNullChecks": false,
|
|
12
|
+
"noImplicitAny": false,
|
|
13
|
+
"strictBindCallApply": false,
|
|
14
|
+
"strictFunctionTypes": false,
|
|
15
|
+
"strictPropertyInitialization": false,
|
|
16
|
+
"noImplicitThis": false,
|
|
17
|
+
"alwaysStrict": false,
|
|
18
|
+
"forceConsistentCasingInFileNames": false,
|
|
19
|
+
"noEmit": true,
|
|
20
|
+
"noEmitOnError": false,
|
|
21
|
+
"esModuleInterop": true,
|
|
22
|
+
"module": "esnext",
|
|
23
|
+
"moduleResolution": "bundler",
|
|
24
|
+
"resolveJsonModule": true,
|
|
25
|
+
"isolatedModules": true,
|
|
26
|
+
"jsx": "preserve",
|
|
27
|
+
"incremental": true,
|
|
28
|
+
"noUnusedLocals": false,
|
|
29
|
+
"noUnusedParameters": false,
|
|
30
|
+
"noImplicitReturns": false,
|
|
31
|
+
"declaration": false,
|
|
32
|
+
"declarationMap": false,
|
|
33
|
+
"plugins": [
|
|
34
|
+
{
|
|
35
|
+
"name": "next"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"paths": {
|
|
39
|
+
"@/*": ["./src/*"]
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"include": [
|
|
43
|
+
"next-env.d.ts",
|
|
44
|
+
"**/*.ts",
|
|
45
|
+
"**/*.tsx",
|
|
46
|
+
"**/*.mdx",
|
|
47
|
+
"**/*.test.ts",
|
|
48
|
+
".next/types/**/*.ts"
|
|
49
|
+
],
|
|
50
|
+
"exclude": ["node_modules", "jest.setup.tsx"]
|
|
51
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# ASSETTRACK MONOREPO - DOCKER COMPOSE CONFIGURATION
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# This docker-compose.yml references environment variables from the root .env file
|
|
5
|
+
# It can also work with Coolify where variables are injected via Coolify's UI
|
|
6
|
+
#
|
|
7
|
+
# IMPORTANT: Infrastructure services (Neo4j, Redis, MinIO) are expected to run
|
|
8
|
+
# externally and are configured via the .env file connection strings.
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# docker compose up # Start all three services (api, worker, web)
|
|
12
|
+
# docker compose up api # Start only API service
|
|
13
|
+
# docker compose up worker # Start only Worker service
|
|
14
|
+
# docker compose up web # Start only Web service
|
|
15
|
+
# docker compose up api worker # Start API and Worker services
|
|
16
|
+
# =============================================================================
|
|
17
|
+
x-api-worker-shared-env: &api_worker_shared_env
|
|
18
|
+
# Database - Use .env values (external services)
|
|
19
|
+
NEO4J_URI: ${NEO4J_URI}
|
|
20
|
+
NEO4J_USER: ${NEO4J_USER}
|
|
21
|
+
NEO4J_PASSWORD: ${NEO4J_PASSWORD}
|
|
22
|
+
NEO4J_DATABASE: ${NEO4J_DATABASE}
|
|
23
|
+
|
|
24
|
+
# Cache & Queue - Use .env values (external services)
|
|
25
|
+
REDIS_HOST: ${REDIS_HOST}
|
|
26
|
+
REDIS_PORT: ${REDIS_PORT}
|
|
27
|
+
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
|
28
|
+
REDIS_USERNAME: ${REDIS_USERNAME}
|
|
29
|
+
REDIS_QUEUE: ${REDIS_QUEUE}
|
|
30
|
+
|
|
31
|
+
# Authentication
|
|
32
|
+
JWT_SECRET: ${JWT_SECRET}
|
|
33
|
+
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN}
|
|
34
|
+
|
|
35
|
+
# Web Push - VAPID
|
|
36
|
+
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
|
37
|
+
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY}
|
|
38
|
+
VAPID_EMAIL: ${VAPID_EMAIL}
|
|
39
|
+
|
|
40
|
+
# Email Configuration
|
|
41
|
+
EMAIL_PROVIDER: ${EMAIL_PROVIDER}
|
|
42
|
+
EMAIL_API_KEY: ${EMAIL_API_KEY}
|
|
43
|
+
EMAIL_FROM: ${EMAIL_FROM}
|
|
44
|
+
EMAIL_HOST: ${EMAIL_HOST}
|
|
45
|
+
EMAIL_PORT: ${EMAIL_PORT}
|
|
46
|
+
EMAIL_SECURE: ${EMAIL_SECURE}
|
|
47
|
+
EMAIL_USERNAME: ${EMAIL_USERNAME}
|
|
48
|
+
EMAIL_PASSWORD: ${EMAIL_PASSWORD}
|
|
49
|
+
|
|
50
|
+
# Logging - Loki
|
|
51
|
+
LOKI_ENABLED: ${LOKI_ENABLED}
|
|
52
|
+
LOKI_HOST: ${LOKI_HOST}
|
|
53
|
+
LOKI_USERNAME: ${LOKI_USERNAME}
|
|
54
|
+
LOKI_PASSWORD: ${LOKI_PASSWORD}
|
|
55
|
+
LOKI_APP_LABEL: ${LOKI_APP_LABEL}
|
|
56
|
+
|
|
57
|
+
# Tracing - Tempo
|
|
58
|
+
TEMPO_ENABLED: ${TEMPO_ENABLED}
|
|
59
|
+
TEMPO_ENDPOINT: ${TEMPO_ENDPOINT}
|
|
60
|
+
TEMPO_SERVICE_NAME: ${TEMPO_SERVICE_NAME}
|
|
61
|
+
TEMPO_SERVICE_VERSION: ${TEMPO_SERVICE_VERSION}
|
|
62
|
+
|
|
63
|
+
# Object Storage - S3/MinIO - Use .env values (external services)
|
|
64
|
+
S3_TYPE: ${S3_TYPE}
|
|
65
|
+
S3_ENDPOINT: ${S3_ENDPOINT}
|
|
66
|
+
S3_BUCKET: ${S3_BUCKET}
|
|
67
|
+
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID}
|
|
68
|
+
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY}
|
|
69
|
+
S3_REGION: ${S3_REGION}
|
|
70
|
+
|
|
71
|
+
# ai
|
|
72
|
+
AI_PROVIDER: ${AI_PROVIDER}
|
|
73
|
+
AI_API_KEY: ${AI_API_KEY}
|
|
74
|
+
AI_MODEL: ${AI_MODEL}
|
|
75
|
+
AI_URL: ${AI_URL}
|
|
76
|
+
AI_REGION: ${AI_REGION}
|
|
77
|
+
AI_SECRET: ${AI_SECRET}
|
|
78
|
+
AI_INSTANCE: ${AI_INSTANCE}
|
|
79
|
+
AI_API_VERSION: ${AI_API_VERSION}
|
|
80
|
+
AI_INPUT_COST_PER_1M_TOKENS: ${AI_INPUT_COST_PER_1M_TOKENS}
|
|
81
|
+
AI_OUTPUT_COST_PER_1M_TOKENS: ${AI_OUTPUT_COST_PER_1M_TOKENS}
|
|
82
|
+
|
|
83
|
+
#transcriber
|
|
84
|
+
TRANSCRIBER_PROVIDER: ${TRANSCRIBER_PROVIDER}
|
|
85
|
+
TRANSCRIBER_API_KEY: ${TRANSCRIBER_API_KEY}
|
|
86
|
+
TRANSCRIBER_MODEL: ${TRANSCRIBER_MODEL}
|
|
87
|
+
TRANSCRIBER_URL: ${TRANSCRIBER_URL}
|
|
88
|
+
TRANSCRIBER_API_VERSION: ${TRANSCRIBER_API_VERSION}
|
|
89
|
+
|
|
90
|
+
# embedder
|
|
91
|
+
EMBEDDER_PROVIDER: ${EMBEDDER_PROVIDER}
|
|
92
|
+
EMBEDDER_API_KEY: ${EMBEDDER_API_KEY}
|
|
93
|
+
EMBEDDER_MODEL: ${EMBEDDER_MODEL}
|
|
94
|
+
EMBEDDER_INSTANCE: ${EMBEDDER_INSTANCE}
|
|
95
|
+
EMBEDDER_API_VERSION: ${EMBEDDER_API_VERSION}
|
|
96
|
+
EMBEDDER_DIMENSIONS: ${EMBEDDER_DIMENSIONS}
|
|
97
|
+
|
|
98
|
+
# =============================================================================
|
|
99
|
+
|
|
100
|
+
services:
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# API Server (NestJS Backend)
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
api:
|
|
105
|
+
build:
|
|
106
|
+
context: .
|
|
107
|
+
dockerfile: Dockerfile
|
|
108
|
+
target: api-production
|
|
109
|
+
args:
|
|
110
|
+
- API_PORT=${API_PORT:-3400}
|
|
111
|
+
ports:
|
|
112
|
+
- "${API_PORT:-3400}:${API_PORT:-3400}"
|
|
113
|
+
environment:
|
|
114
|
+
<<: *api_worker_shared_env
|
|
115
|
+
# Build & Application Mode
|
|
116
|
+
PORT: ${API_PORT:-3400}
|
|
117
|
+
API_URL: ${API_URL}
|
|
118
|
+
API_PORT: ${API_PORT:-3400}
|
|
119
|
+
NODE_OPTIONS: ${API_NODE_OPTIONS:-"--max-old-space-size=6144"}
|
|
120
|
+
|
|
121
|
+
# Cache Configuration
|
|
122
|
+
CACHE_ENABLED: ${CACHE_ENABLED}
|
|
123
|
+
CACHE_DEFAULT_TTL: ${CACHE_DEFAULT_TTL}
|
|
124
|
+
CACHE_SKIP_PATTERNS: ${CACHE_SKIP_PATTERNS}
|
|
125
|
+
|
|
126
|
+
# Rate Limiting
|
|
127
|
+
RATE_LIMIT_ENABLED: ${RATE_LIMIT_ENABLED}
|
|
128
|
+
RATE_LIMIT_TTL: ${RATE_LIMIT_TTL}
|
|
129
|
+
RATE_LIMIT_REQUESTS: ${RATE_LIMIT_REQUESTS}
|
|
130
|
+
IP_RATE_LIMIT_REQUESTS: ${IP_RATE_LIMIT_REQUESTS}
|
|
131
|
+
|
|
132
|
+
# CORS Configuration
|
|
133
|
+
CORS_ORIGINS: ${CORS_ORIGINS}
|
|
134
|
+
CORS_ORIGIN_PATTERNS: ${CORS_ORIGIN_PATTERNS}
|
|
135
|
+
CORS_CREDENTIALS: ${CORS_CREDENTIALS}
|
|
136
|
+
CORS_METHODS: ${CORS_METHODS}
|
|
137
|
+
CORS_ALLOWED_HEADERS: ${CORS_ALLOWED_HEADERS}
|
|
138
|
+
CORS_MAX_AGE: ${CORS_MAX_AGE}
|
|
139
|
+
CORS_PREFLIGHT_CONTINUE: ${CORS_PREFLIGHT_CONTINUE}
|
|
140
|
+
CORS_OPTIONS_SUCCESS_STATUS: ${CORS_OPTIONS_SUCCESS_STATUS}
|
|
141
|
+
CORS_LOG_VIOLATIONS: ${CORS_LOG_VIOLATIONS}
|
|
142
|
+
healthcheck:
|
|
143
|
+
test: ["CMD", "node", "-e", "require('http').get({host:'localhost',port:process.env.API_PORT||3400,path:'/version'},(r)=>process.exit(r.statusCode>=200&&r.statusCode<500?0:1))"]
|
|
144
|
+
interval: 10s
|
|
145
|
+
timeout: 5s
|
|
146
|
+
retries: 3
|
|
147
|
+
start_period: 40s
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------------
|
|
150
|
+
# Worker (Background Job Processing)
|
|
151
|
+
# ---------------------------------------------------------------------------
|
|
152
|
+
worker:
|
|
153
|
+
build:
|
|
154
|
+
context: .
|
|
155
|
+
dockerfile: Dockerfile
|
|
156
|
+
target: api-production
|
|
157
|
+
environment:
|
|
158
|
+
<<: *api_worker_shared_env
|
|
159
|
+
NODE_OPTIONS: ${WORKER_NODE_OPTIONS:-"--max-old-space-size=6144"}
|
|
160
|
+
healthcheck:
|
|
161
|
+
test: ["CMD", "pgrep", "-f", "node.*dist/main.*worker"]
|
|
162
|
+
interval: 10s
|
|
163
|
+
timeout: 5s
|
|
164
|
+
retries: 3
|
|
165
|
+
start_period: 30s
|
|
166
|
+
command:
|
|
167
|
+
- node
|
|
168
|
+
- dist/main
|
|
169
|
+
- --mode=worker
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
# Web Application (Next.js Frontend)
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
web:
|
|
175
|
+
build:
|
|
176
|
+
context: .
|
|
177
|
+
dockerfile: Dockerfile
|
|
178
|
+
target: web-production
|
|
179
|
+
args:
|
|
180
|
+
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
|
181
|
+
- NEXT_PUBLIC_ADDRESS=${NEXT_PUBLIC_ADDRESS}
|
|
182
|
+
- NEXT_PUBLIC_VAPID_PUBLIC_KEY=${NEXT_PUBLIC_VAPID_PUBLIC_KEY}
|
|
183
|
+
- NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}
|
|
184
|
+
- PORT=${PORT:-3401}
|
|
185
|
+
ports:
|
|
186
|
+
- "${PORT:-3401}:${PORT:-3401}"
|
|
187
|
+
environment:
|
|
188
|
+
# Build & Application Configuration
|
|
189
|
+
- PORT=${PORT:-3401}
|
|
190
|
+
- APP_URL=${APP_URL}
|
|
191
|
+
- NODE_OPTIONS=${WEB_NODE_OPTIONS:-"--max-old-space-size=4096"}
|
|
192
|
+
|
|
193
|
+
# API Connection
|
|
194
|
+
- NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
|
|
195
|
+
- NEXT_PUBLIC_ADDRESS=${NEXT_PUBLIC_ADDRESS}
|
|
196
|
+
- API_INTERNAL_URL=${API_INTERNAL_URL:-http://api:3400/}
|
|
197
|
+
|
|
198
|
+
# Web Push - VAPID
|
|
199
|
+
- NEXT_PUBLIC_VAPID_PUBLIC_KEY=${NEXT_PUBLIC_VAPID_PUBLIC_KEY}
|
|
200
|
+
|
|
201
|
+
# Image Sources
|
|
202
|
+
- IMAGE_SOURCES=${IMAGE_SOURCES}
|
|
203
|
+
|
|
204
|
+
# Google Maps API Key
|
|
205
|
+
- NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${NEXT_PUBLIC_GOOGLE_MAPS_API_KEY}
|
|
206
|
+
healthcheck:
|
|
207
|
+
test: ["CMD", "node", "-e", "require('http').get({host:'localhost',port:process.env.PORT||3401,path:'/'},(r)=>process.exit(r.statusCode>=200&&r.statusCode<500?0:1))"]
|
|
208
|
+
interval: 10s
|
|
209
|
+
timeout: 5s
|
|
210
|
+
retries: 3
|
|
211
|
+
start_period: 40s
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "Role Playing Game platform powered by AI",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22.0.0",
|
|
8
|
+
"pnpm": ">=10.0.0"
|
|
9
|
+
},
|
|
10
|
+
"packageManager": "pnpm@10.25.0",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "turbo run dev dev:worker",
|
|
13
|
+
"dev:api": "turbo run dev --filter={{name}}-api",
|
|
14
|
+
"dev:web": "turbo run dev --filter={{name}}-web",
|
|
15
|
+
"dev:worker": "turbo run start:worker:dev --filter={{name}}-api",
|
|
16
|
+
"build": "turbo run build",
|
|
17
|
+
"build:api": "turbo run build --filter={{name}}-api",
|
|
18
|
+
"build:web": "turbo run build --filter={{name}}-web",
|
|
19
|
+
"start": "turbo run start",
|
|
20
|
+
"start:prod": "turbo run start:prod",
|
|
21
|
+
"test": "turbo run test",
|
|
22
|
+
"test:watch": "turbo run test:watch",
|
|
23
|
+
"test:e2e": "turbo run test:e2e",
|
|
24
|
+
"test:cov": "turbo run test:cov",
|
|
25
|
+
"lint": "turbo run lint",
|
|
26
|
+
"format": "turbo run format",
|
|
27
|
+
"clean": "turbo run clean && rm -rf node_modules .turbo",
|
|
28
|
+
"clean:all": "pnpm clean && pnpm -r exec rm -rf node_modules dist .next .turbo",
|
|
29
|
+
"structure": "bash scripts/import-structure.sh",
|
|
30
|
+
"prepare": "husky",
|
|
31
|
+
"preinstall": "npx only-allow pnpm",
|
|
32
|
+
"neo4jsonapi-migrate": "node ./packages/nestjs-neo4jsonapi/dist/tools/migrate-entity/index.js"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"dotenv-cli": "^11.0.0",
|
|
36
|
+
"zod": "^4.1.13"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@eslint/eslintrc": "^3.3.3",
|
|
40
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
41
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
42
|
+
"@semantic-release/git": "^10.0.1",
|
|
43
|
+
"@semantic-release/npm": "^13.1.2",
|
|
44
|
+
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
45
|
+
"@types/node": "^25.0.1",
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
|
47
|
+
"@typescript-eslint/parser": "^8.49.0",
|
|
48
|
+
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
49
|
+
"eslint": "^9.39.1",
|
|
50
|
+
"eslint-config-prettier": "^10.1.8",
|
|
51
|
+
"eslint-plugin-prettier": "^5.5.4",
|
|
52
|
+
"husky": "^9.1.7",
|
|
53
|
+
"prettier": "^3.7.4",
|
|
54
|
+
"semantic-release": "^25.0.2",
|
|
55
|
+
"ts-node": "^10.9.2",
|
|
56
|
+
"turbo": "^2.6.3",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
},
|
|
59
|
+
"workspaces": [
|
|
60
|
+
"api",
|
|
61
|
+
"web",
|
|
62
|
+
"packages/*"
|
|
63
|
+
],
|
|
64
|
+
"pnpm": {
|
|
65
|
+
"overrides": {
|
|
66
|
+
"yjs": "^13.6.27",
|
|
67
|
+
"validator": ">=13.15.20",
|
|
68
|
+
"@types/react": "19.2.7",
|
|
69
|
+
"@types/react-dom": "19.2.3"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{name}}/shared",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsup --watch",
|
|
18
|
+
"clean": "rm -rf dist"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"tsup": "^8.5.0"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { RoleId } from "./const/roles.id";
|