@ezetgalaxy/titan 26.8.3 → 26.9.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/README.md +63 -14
- package/index.js +62 -15
- package/package.json +1 -1
- package/templates/extension/README.md +104 -104
- package/templates/extension/index.js +27 -27
- package/templates/extension/jsconfig.json +12 -12
- package/templates/extension/native/Cargo.toml +9 -9
- package/templates/extension/native/src/lib.rs +5 -5
- package/templates/extension/package.json +20 -20
- package/templates/extension/titan.json +17 -17
- package/templates/js/Dockerfile +66 -66
- package/templates/js/_dockerignore +3 -3
- package/templates/js/_gitignore +1 -0
- package/templates/js/app/actions/hello.js +5 -5
- package/templates/js/app/titan.d.ts +87 -87
- package/templates/js/jsconfig.json +18 -18
- package/templates/js/server/src/action_management.rs +131 -131
- package/templates/js/server/src/errors.rs +10 -10
- package/templates/js/server/src/extensions.rs +989 -989
- package/templates/js/server/src/utils.rs +33 -33
- package/templates/js/titan/bundle.js +78 -78
- package/templates/js/titan/dev.js +9 -1
- package/templates/js/titan/titan.js +122 -122
- package/templates/rust/Dockerfile +66 -66
- package/templates/rust/_dockerignore +3 -3
- package/templates/rust/_gitignore +1 -0
- package/templates/rust/app/actions/hello.js +5 -5
- package/templates/rust/app/actions/rust_hello.rs +14 -14
- package/templates/rust/app/titan.d.ts +101 -101
- package/templates/rust/jsconfig.json +18 -18
- package/templates/rust/server/src/action_management.rs +131 -131
- package/templates/rust/server/src/errors.rs +10 -10
- package/templates/rust/server/src/extensions.rs +989 -989
- package/templates/rust/server/src/utils.rs +33 -33
- package/templates/rust/titan/dev.js +9 -1
- package/templates/rust-ts/Dockerfile +66 -0
- package/templates/rust-ts/_dockerignore +3 -0
- package/templates/rust-ts/_gitignore +38 -0
- package/templates/rust-ts/app/actions/hello.ts +5 -0
- package/templates/rust-ts/app/actions/rust_hello.rs +14 -0
- package/templates/rust-ts/app/app.ts +11 -0
- package/templates/rust-ts/app/titan.d.ts +101 -0
- package/templates/rust-ts/package.json +14 -0
- package/templates/rust-ts/server/Cargo.lock +2869 -0
- package/templates/rust-ts/server/Cargo.toml +39 -0
- package/templates/rust-ts/server/src/action_management.rs +131 -0
- package/templates/rust-ts/server/src/errors.rs +51 -0
- package/templates/rust-ts/server/src/extensions.rs +989 -0
- package/templates/rust-ts/server/src/main.rs +468 -0
- package/templates/rust-ts/server/src/utils.rs +33 -0
- package/templates/rust-ts/titan/bundle.js +157 -0
- package/templates/rust-ts/titan/dev.js +402 -0
- package/templates/rust-ts/titan/titan.js +122 -0
- package/templates/rust-ts/tsconfig.json +21 -0
- package/templates/ts/Dockerfile +66 -0
- package/templates/ts/_dockerignore +3 -0
- package/templates/ts/_gitignore +38 -0
- package/templates/ts/app/actions/hello.ts +9 -0
- package/templates/ts/app/app.ts +10 -0
- package/templates/ts/app/titan.d.ts +102 -0
- package/templates/ts/package.json +26 -0
- package/templates/ts/server/Cargo.lock +2869 -0
- package/templates/ts/server/Cargo.toml +27 -0
- package/templates/ts/server/src/action_management.rs +131 -0
- package/templates/ts/server/src/errors.rs +51 -0
- package/templates/ts/server/src/extensions.rs +989 -0
- package/templates/ts/server/src/main.rs +437 -0
- package/templates/ts/server/src/utils.rs +33 -0
- package/templates/ts/titan/bundle.js +78 -0
- package/templates/ts/titan/dev.js +402 -0
- package/templates/ts/titan/titan.js +122 -0
- package/templates/ts/tsconfig.json +16 -0
- package/titanpl-sdk/README.md +109 -109
- package/titanpl-sdk/bin/run.js +254 -254
- package/titanpl-sdk/index.d.ts +46 -46
- package/titanpl-sdk/index.js +5 -5
- package/titanpl-sdk/package.json +32 -32
- package/titanpl-sdk/templates/.dockerignore +3 -3
- package/titanpl-sdk/templates/Dockerfile +53 -53
- package/titanpl-sdk/templates/app/actions/hello.js +5 -5
- package/titanpl-sdk/templates/app/titan.d.ts +87 -87
- package/titanpl-sdk/templates/jsconfig.json +18 -18
- package/titanpl-sdk/templates/server/src/action_management.rs +131 -131
- package/titanpl-sdk/templates/server/src/errors.rs +10 -10
- package/titanpl-sdk/templates/server/src/extensions.rs +640 -640
- package/titanpl-sdk/templates/server/src/utils.rs +33 -33
- package/titanpl-sdk/templates/titan/bundle.js +65 -65
- package/titanpl-sdk/templates/titan/dev.js +113 -113
- package/titanpl-sdk/templates/titan/titan.js +98 -98
- package/templates/js/server/action_map.json +0 -3
- package/templates/js/server/actions/hello.jsbundle +0 -48
- package/templates/js/server/routes.json +0 -16
- package/templates/rust/server/action_map.json +0 -3
- package/templates/rust/server/actions/hello.jsbundle +0 -47
- package/templates/rust/server/routes.json +0 -22
- package/templates/rust/server/src/actions_rust/mod.rs +0 -19
- package/templates/rust/server/src/actions_rust/rust_hello.rs +0 -14
package/titanpl-sdk/package.json
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "titanpl-sdk",
|
|
3
|
-
"version": "0.1.6",
|
|
4
|
-
"description": "Development SDK for Titan Planet. Provides TypeScript type definitions for the global 't' runtime object and a 'lite' test-harness runtime for building and verifying extensions.",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"types": "index.d.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"titanpl-sdk": "./bin/run.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"bin/",
|
|
13
|
-
"templates/",
|
|
14
|
-
"titan",
|
|
15
|
-
"assets",
|
|
16
|
-
"index.js",
|
|
17
|
-
"index.d.ts",
|
|
18
|
-
"README.md"
|
|
19
|
-
],
|
|
20
|
-
"keywords": [
|
|
21
|
-
"titan",
|
|
22
|
-
"titan-planet",
|
|
23
|
-
"titanpl-sdk",
|
|
24
|
-
"ezetgalaxy",
|
|
25
|
-
"types",
|
|
26
|
-
"typescript",
|
|
27
|
-
"intellisense",
|
|
28
|
-
"sdk",
|
|
29
|
-
"backend-sdk",
|
|
30
|
-
"extension-development"
|
|
31
|
-
],
|
|
32
|
-
"license": "ISC"
|
|
1
|
+
{
|
|
2
|
+
"name": "titanpl-sdk",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Development SDK for Titan Planet. Provides TypeScript type definitions for the global 't' runtime object and a 'lite' test-harness runtime for building and verifying extensions.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"titanpl-sdk": "./bin/run.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"titan",
|
|
15
|
+
"assets",
|
|
16
|
+
"index.js",
|
|
17
|
+
"index.d.ts",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"keywords": [
|
|
21
|
+
"titan",
|
|
22
|
+
"titan-planet",
|
|
23
|
+
"titanpl-sdk",
|
|
24
|
+
"ezetgalaxy",
|
|
25
|
+
"types",
|
|
26
|
+
"typescript",
|
|
27
|
+
"intellisense",
|
|
28
|
+
"sdk",
|
|
29
|
+
"backend-sdk",
|
|
30
|
+
"extension-development"
|
|
31
|
+
],
|
|
32
|
+
"license": "ISC"
|
|
33
33
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
node_modules
|
|
2
|
-
npm-debug.log
|
|
3
|
-
.git
|
|
1
|
+
node_modules
|
|
2
|
+
npm-debug.log
|
|
3
|
+
.git
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
# ================================================================
|
|
2
|
-
# STAGE 1 — Build Titan (JS → Rust)
|
|
3
|
-
# ================================================================
|
|
4
|
-
FROM rust:1.91.1 AS builder
|
|
5
|
-
|
|
6
|
-
# Install Node for Titan CLI + bundler
|
|
7
|
-
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
|
8
|
-
&& apt-get install -y nodejs
|
|
9
|
-
|
|
10
|
-
# Install Titan CLI (latest)
|
|
11
|
-
RUN npm install -g @ezetgalaxy/titan@latest
|
|
12
|
-
|
|
13
|
-
WORKDIR /app
|
|
14
|
-
|
|
15
|
-
# Copy project files
|
|
16
|
-
COPY . .
|
|
17
|
-
|
|
18
|
-
# Install JS dependencies (needed for Titan DSL + bundler)
|
|
19
|
-
RUN npm install
|
|
20
|
-
|
|
21
|
-
# Build Titan metadata + bundle JS actions
|
|
22
|
-
RUN titan build
|
|
23
|
-
|
|
24
|
-
# Build Rust binary
|
|
25
|
-
RUN cd server && cargo build --release
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# ================================================================
|
|
30
|
-
# STAGE 2 — Runtime Image (Lightweight)
|
|
31
|
-
# ================================================================
|
|
32
|
-
FROM debian:stable-slim
|
|
33
|
-
|
|
34
|
-
WORKDIR /app
|
|
35
|
-
|
|
36
|
-
# Copy Rust binary from builder stage
|
|
37
|
-
COPY --from=builder /app/server/target/release/server ./titan-server
|
|
38
|
-
|
|
39
|
-
# Copy Titan routing metadata
|
|
40
|
-
COPY --from=builder /app/server/routes.json ./routes.json
|
|
41
|
-
COPY --from=builder /app/server/action_map.json ./action_map.json
|
|
42
|
-
|
|
43
|
-
# Copy Titan JS bundles
|
|
44
|
-
RUN mkdir -p /app/actions
|
|
45
|
-
COPY --from=builder /app/server/actions /app/actions
|
|
46
|
-
|
|
47
|
-
COPY --from=builder /app/db /app/assets
|
|
48
|
-
|
|
49
|
-
# Expose Titan port
|
|
50
|
-
EXPOSE 3000
|
|
51
|
-
|
|
52
|
-
# Start Titan
|
|
53
|
-
CMD ["./titan-server"]
|
|
1
|
+
# ================================================================
|
|
2
|
+
# STAGE 1 — Build Titan (JS → Rust)
|
|
3
|
+
# ================================================================
|
|
4
|
+
FROM rust:1.91.1 AS builder
|
|
5
|
+
|
|
6
|
+
# Install Node for Titan CLI + bundler
|
|
7
|
+
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
|
8
|
+
&& apt-get install -y nodejs
|
|
9
|
+
|
|
10
|
+
# Install Titan CLI (latest)
|
|
11
|
+
RUN npm install -g @ezetgalaxy/titan@latest
|
|
12
|
+
|
|
13
|
+
WORKDIR /app
|
|
14
|
+
|
|
15
|
+
# Copy project files
|
|
16
|
+
COPY . .
|
|
17
|
+
|
|
18
|
+
# Install JS dependencies (needed for Titan DSL + bundler)
|
|
19
|
+
RUN npm install
|
|
20
|
+
|
|
21
|
+
# Build Titan metadata + bundle JS actions
|
|
22
|
+
RUN titan build
|
|
23
|
+
|
|
24
|
+
# Build Rust binary
|
|
25
|
+
RUN cd server && cargo build --release
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ================================================================
|
|
30
|
+
# STAGE 2 — Runtime Image (Lightweight)
|
|
31
|
+
# ================================================================
|
|
32
|
+
FROM debian:stable-slim
|
|
33
|
+
|
|
34
|
+
WORKDIR /app
|
|
35
|
+
|
|
36
|
+
# Copy Rust binary from builder stage
|
|
37
|
+
COPY --from=builder /app/server/target/release/server ./titan-server
|
|
38
|
+
|
|
39
|
+
# Copy Titan routing metadata
|
|
40
|
+
COPY --from=builder /app/server/routes.json ./routes.json
|
|
41
|
+
COPY --from=builder /app/server/action_map.json ./action_map.json
|
|
42
|
+
|
|
43
|
+
# Copy Titan JS bundles
|
|
44
|
+
RUN mkdir -p /app/actions
|
|
45
|
+
COPY --from=builder /app/server/actions /app/actions
|
|
46
|
+
|
|
47
|
+
COPY --from=builder /app/db /app/assets
|
|
48
|
+
|
|
49
|
+
# Expose Titan port
|
|
50
|
+
EXPOSE 3000
|
|
51
|
+
|
|
52
|
+
# Start Titan
|
|
53
|
+
CMD ["./titan-server"]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export const hello = (req) => {
|
|
2
|
-
return {
|
|
3
|
-
message: `Hello from Titan ${req.body.name}`,
|
|
4
|
-
};
|
|
5
|
-
}
|
|
1
|
+
export const hello = (req) => {
|
|
2
|
+
return {
|
|
3
|
+
message: `Hello from Titan ${req.body.name}`,
|
|
4
|
+
};
|
|
5
|
+
}
|
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TITAN TYPE DEFINITIONS
|
|
3
|
-
* ----------------------
|
|
4
|
-
* These types are globally available in your Titan project.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The Titan Request Object passed to actions.
|
|
9
|
-
*/
|
|
10
|
-
interface TitanRequest {
|
|
11
|
-
body: any;
|
|
12
|
-
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
13
|
-
path: string;
|
|
14
|
-
headers: {
|
|
15
|
-
host?: string;
|
|
16
|
-
"content-type"?: string;
|
|
17
|
-
"user-agent"?: string;
|
|
18
|
-
authorization?: string;
|
|
19
|
-
[key: string]: string | undefined;
|
|
20
|
-
};
|
|
21
|
-
params: Record<string, string>;
|
|
22
|
-
query: Record<string, string>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface DbConnection {
|
|
26
|
-
/**
|
|
27
|
-
* Execute a SQL query.
|
|
28
|
-
* @param sql The SQL query string.
|
|
29
|
-
* @param params (Optional) Parameters for the query ($1, $2, etc).
|
|
30
|
-
*/
|
|
31
|
-
query(sql: string, params?: any[]): any[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Define a Titan Action with type inference.
|
|
36
|
-
* @example
|
|
37
|
-
* export const hello = defineAction((req) => {
|
|
38
|
-
* return req.headers;
|
|
39
|
-
* });
|
|
40
|
-
*/
|
|
41
|
-
declare function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Titan Runtime Utilities
|
|
45
|
-
*/
|
|
46
|
-
declare const t: {
|
|
47
|
-
/**
|
|
48
|
-
* Log messages to the server console with Titan formatting.
|
|
49
|
-
*/
|
|
50
|
-
log(...args: any[]): void;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Read a file contents as string.
|
|
54
|
-
* @param path Relative path to the file from project root.
|
|
55
|
-
*/
|
|
56
|
-
read(path: string): string;
|
|
57
|
-
|
|
58
|
-
fetch(url: string, options?: {
|
|
59
|
-
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
60
|
-
headers?: Record<string, string>;
|
|
61
|
-
body?: string | object;
|
|
62
|
-
}): {
|
|
63
|
-
ok: boolean;
|
|
64
|
-
status?: number;
|
|
65
|
-
body?: string;
|
|
66
|
-
error?: string;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
jwt: {
|
|
70
|
-
sign(
|
|
71
|
-
payload: object,
|
|
72
|
-
secret: string,
|
|
73
|
-
options?: { expiresIn?: string | number }
|
|
74
|
-
): string;
|
|
75
|
-
verify(token: string, secret: string): any;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
password: {
|
|
79
|
-
hash(password: string): string;
|
|
80
|
-
verify(password: string, hash: string): boolean;
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
db: {
|
|
84
|
-
connect(url: string): DbConnection;
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
|
|
1
|
+
/**
|
|
2
|
+
* TITAN TYPE DEFINITIONS
|
|
3
|
+
* ----------------------
|
|
4
|
+
* These types are globally available in your Titan project.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The Titan Request Object passed to actions.
|
|
9
|
+
*/
|
|
10
|
+
interface TitanRequest {
|
|
11
|
+
body: any;
|
|
12
|
+
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
13
|
+
path: string;
|
|
14
|
+
headers: {
|
|
15
|
+
host?: string;
|
|
16
|
+
"content-type"?: string;
|
|
17
|
+
"user-agent"?: string;
|
|
18
|
+
authorization?: string;
|
|
19
|
+
[key: string]: string | undefined;
|
|
20
|
+
};
|
|
21
|
+
params: Record<string, string>;
|
|
22
|
+
query: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface DbConnection {
|
|
26
|
+
/**
|
|
27
|
+
* Execute a SQL query.
|
|
28
|
+
* @param sql The SQL query string.
|
|
29
|
+
* @param params (Optional) Parameters for the query ($1, $2, etc).
|
|
30
|
+
*/
|
|
31
|
+
query(sql: string, params?: any[]): any[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Define a Titan Action with type inference.
|
|
36
|
+
* @example
|
|
37
|
+
* export const hello = defineAction((req) => {
|
|
38
|
+
* return req.headers;
|
|
39
|
+
* });
|
|
40
|
+
*/
|
|
41
|
+
declare function defineAction<T>(actionFn: (req: TitanRequest) => T): (req: TitanRequest) => T;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Titan Runtime Utilities
|
|
45
|
+
*/
|
|
46
|
+
declare const t: {
|
|
47
|
+
/**
|
|
48
|
+
* Log messages to the server console with Titan formatting.
|
|
49
|
+
*/
|
|
50
|
+
log(...args: any[]): void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a file contents as string.
|
|
54
|
+
* @param path Relative path to the file from project root.
|
|
55
|
+
*/
|
|
56
|
+
read(path: string): string;
|
|
57
|
+
|
|
58
|
+
fetch(url: string, options?: {
|
|
59
|
+
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
60
|
+
headers?: Record<string, string>;
|
|
61
|
+
body?: string | object;
|
|
62
|
+
}): {
|
|
63
|
+
ok: boolean;
|
|
64
|
+
status?: number;
|
|
65
|
+
body?: string;
|
|
66
|
+
error?: string;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
jwt: {
|
|
70
|
+
sign(
|
|
71
|
+
payload: object,
|
|
72
|
+
secret: string,
|
|
73
|
+
options?: { expiresIn?: string | number }
|
|
74
|
+
): string;
|
|
75
|
+
verify(token: string, secret: string): any;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
password: {
|
|
79
|
+
hash(password: string): string;
|
|
80
|
+
verify(password: string, hash: string): boolean;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
db: {
|
|
84
|
+
connect(url: string): DbConnection;
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"module": "esnext",
|
|
4
|
-
"target": "esnext",
|
|
5
|
-
"checkJs": false,
|
|
6
|
-
"noImplicitAny": false,
|
|
7
|
-
"allowJs": true,
|
|
8
|
-
"moduleResolution": "node",
|
|
9
|
-
"baseUrl": ".",
|
|
10
|
-
"paths": {
|
|
11
|
-
"*": [
|
|
12
|
-
"./app/*"
|
|
13
|
-
]
|
|
14
|
-
}
|
|
15
|
-
},
|
|
16
|
-
"include": [
|
|
17
|
-
"app/**/*"
|
|
18
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "esnext",
|
|
4
|
+
"target": "esnext",
|
|
5
|
+
"checkJs": false,
|
|
6
|
+
"noImplicitAny": false,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"baseUrl": ".",
|
|
10
|
+
"paths": {
|
|
11
|
+
"*": [
|
|
12
|
+
"./app/*"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"app/**/*"
|
|
18
|
+
]
|
|
19
19
|
}
|
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
use std::collections::HashMap;
|
|
2
|
-
use std::env;
|
|
3
|
-
use std::path::{Path, PathBuf};
|
|
4
|
-
use serde::Deserialize;
|
|
5
|
-
use serde_json::Value;
|
|
6
|
-
|
|
7
|
-
/// Route configuration (loaded from routes.json)
|
|
8
|
-
#[derive(Debug, Deserialize, Clone)]
|
|
9
|
-
pub struct RouteVal {
|
|
10
|
-
pub r#type: String,
|
|
11
|
-
pub value: Value,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
#[derive(Debug, Deserialize, Clone)]
|
|
15
|
-
pub struct DynamicRoute {
|
|
16
|
-
pub method: String,
|
|
17
|
-
pub pattern: String,
|
|
18
|
-
pub action: String,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// -------------------------
|
|
22
|
-
// ACTION DIRECTORY RESOLUTION
|
|
23
|
-
// -------------------------
|
|
24
|
-
|
|
25
|
-
pub fn resolve_actions_dir() -> PathBuf {
|
|
26
|
-
// Respect explicit override first
|
|
27
|
-
if let Ok(override_dir) = env::var("TITAN_ACTIONS_DIR") {
|
|
28
|
-
return PathBuf::from(override_dir);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Production container layout
|
|
32
|
-
if Path::new("/app/actions").exists() {
|
|
33
|
-
return PathBuf::from("/app/actions");
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Try to walk up from the executing binary to discover `<...>/server/actions`
|
|
37
|
-
if let Ok(exe) = std::env::current_exe() {
|
|
38
|
-
if let Some(parent) = exe.parent() {
|
|
39
|
-
if let Some(target_dir) = parent.parent() {
|
|
40
|
-
if let Some(server_dir) = target_dir.parent() {
|
|
41
|
-
let candidate = server_dir.join("actions");
|
|
42
|
-
if candidate.exists() {
|
|
43
|
-
return candidate;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Fall back to local ./actions
|
|
51
|
-
PathBuf::from("./actions")
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/// Try to find the directory that contains compiled action bundles.
|
|
55
|
-
pub fn find_actions_dir(project_root: &PathBuf) -> Option<PathBuf> {
|
|
56
|
-
let candidates = [
|
|
57
|
-
project_root.join("server").join("actions"),
|
|
58
|
-
project_root.join("actions"),
|
|
59
|
-
project_root.join("..").join("server").join("actions"),
|
|
60
|
-
PathBuf::from("/app").join("actions"),
|
|
61
|
-
PathBuf::from("actions"),
|
|
62
|
-
];
|
|
63
|
-
|
|
64
|
-
for p in &candidates {
|
|
65
|
-
if p.exists() && p.is_dir() {
|
|
66
|
-
return Some(p.clone());
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
None
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Dynamic Matcher (Core Logic)
|
|
74
|
-
|
|
75
|
-
pub fn match_dynamic_route(
|
|
76
|
-
method: &str,
|
|
77
|
-
path: &str,
|
|
78
|
-
routes: &[DynamicRoute],
|
|
79
|
-
) -> Option<(String, HashMap<String, String>)> {
|
|
80
|
-
let path_segments: Vec<&str> =
|
|
81
|
-
path.trim_matches('/').split('/').collect();
|
|
82
|
-
|
|
83
|
-
for route in routes {
|
|
84
|
-
if route.method != method {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
let pattern_segments: Vec<&str> =
|
|
89
|
-
route.pattern.trim_matches('/').split('/').collect();
|
|
90
|
-
|
|
91
|
-
if pattern_segments.len() != path_segments.len() {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
let mut params = HashMap::new();
|
|
96
|
-
let mut matched = true;
|
|
97
|
-
|
|
98
|
-
for (pat, val) in pattern_segments.iter().zip(path_segments.iter()) {
|
|
99
|
-
if pat.starts_with(':') {
|
|
100
|
-
let inner = &pat[1..];
|
|
101
|
-
|
|
102
|
-
let (name, ty) = inner
|
|
103
|
-
.split_once('<')
|
|
104
|
-
.map(|(n, t)| (n, t.trim_end_matches('>')))
|
|
105
|
-
.unwrap_or((inner, "string"));
|
|
106
|
-
|
|
107
|
-
let valid = match ty {
|
|
108
|
-
"number" => val.parse::<i64>().is_ok(),
|
|
109
|
-
"string" => true,
|
|
110
|
-
_ => false,
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
if !valid {
|
|
114
|
-
matched = false;
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
params.insert(name.to_string(), (*val).to_string());
|
|
119
|
-
} else if pat != val {
|
|
120
|
-
matched = false;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if matched {
|
|
126
|
-
return Some((route.action.clone(), params));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
None
|
|
131
|
-
}
|
|
1
|
+
use std::collections::HashMap;
|
|
2
|
+
use std::env;
|
|
3
|
+
use std::path::{Path, PathBuf};
|
|
4
|
+
use serde::Deserialize;
|
|
5
|
+
use serde_json::Value;
|
|
6
|
+
|
|
7
|
+
/// Route configuration (loaded from routes.json)
|
|
8
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
9
|
+
pub struct RouteVal {
|
|
10
|
+
pub r#type: String,
|
|
11
|
+
pub value: Value,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
15
|
+
pub struct DynamicRoute {
|
|
16
|
+
pub method: String,
|
|
17
|
+
pub pattern: String,
|
|
18
|
+
pub action: String,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// -------------------------
|
|
22
|
+
// ACTION DIRECTORY RESOLUTION
|
|
23
|
+
// -------------------------
|
|
24
|
+
|
|
25
|
+
pub fn resolve_actions_dir() -> PathBuf {
|
|
26
|
+
// Respect explicit override first
|
|
27
|
+
if let Ok(override_dir) = env::var("TITAN_ACTIONS_DIR") {
|
|
28
|
+
return PathBuf::from(override_dir);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Production container layout
|
|
32
|
+
if Path::new("/app/actions").exists() {
|
|
33
|
+
return PathBuf::from("/app/actions");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Try to walk up from the executing binary to discover `<...>/server/actions`
|
|
37
|
+
if let Ok(exe) = std::env::current_exe() {
|
|
38
|
+
if let Some(parent) = exe.parent() {
|
|
39
|
+
if let Some(target_dir) = parent.parent() {
|
|
40
|
+
if let Some(server_dir) = target_dir.parent() {
|
|
41
|
+
let candidate = server_dir.join("actions");
|
|
42
|
+
if candidate.exists() {
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Fall back to local ./actions
|
|
51
|
+
PathBuf::from("./actions")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Try to find the directory that contains compiled action bundles.
|
|
55
|
+
pub fn find_actions_dir(project_root: &PathBuf) -> Option<PathBuf> {
|
|
56
|
+
let candidates = [
|
|
57
|
+
project_root.join("server").join("actions"),
|
|
58
|
+
project_root.join("actions"),
|
|
59
|
+
project_root.join("..").join("server").join("actions"),
|
|
60
|
+
PathBuf::from("/app").join("actions"),
|
|
61
|
+
PathBuf::from("actions"),
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
for p in &candidates {
|
|
65
|
+
if p.exists() && p.is_dir() {
|
|
66
|
+
return Some(p.clone());
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
None
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Dynamic Matcher (Core Logic)
|
|
74
|
+
|
|
75
|
+
pub fn match_dynamic_route(
|
|
76
|
+
method: &str,
|
|
77
|
+
path: &str,
|
|
78
|
+
routes: &[DynamicRoute],
|
|
79
|
+
) -> Option<(String, HashMap<String, String>)> {
|
|
80
|
+
let path_segments: Vec<&str> =
|
|
81
|
+
path.trim_matches('/').split('/').collect();
|
|
82
|
+
|
|
83
|
+
for route in routes {
|
|
84
|
+
if route.method != method {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let pattern_segments: Vec<&str> =
|
|
89
|
+
route.pattern.trim_matches('/').split('/').collect();
|
|
90
|
+
|
|
91
|
+
if pattern_segments.len() != path_segments.len() {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let mut params = HashMap::new();
|
|
96
|
+
let mut matched = true;
|
|
97
|
+
|
|
98
|
+
for (pat, val) in pattern_segments.iter().zip(path_segments.iter()) {
|
|
99
|
+
if pat.starts_with(':') {
|
|
100
|
+
let inner = &pat[1..];
|
|
101
|
+
|
|
102
|
+
let (name, ty) = inner
|
|
103
|
+
.split_once('<')
|
|
104
|
+
.map(|(n, t)| (n, t.trim_end_matches('>')))
|
|
105
|
+
.unwrap_or((inner, "string"));
|
|
106
|
+
|
|
107
|
+
let valid = match ty {
|
|
108
|
+
"number" => val.parse::<i64>().is_ok(),
|
|
109
|
+
"string" => true,
|
|
110
|
+
_ => false,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
if !valid {
|
|
114
|
+
matched = false;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
params.insert(name.to_string(), (*val).to_string());
|
|
119
|
+
} else if pat != val {
|
|
120
|
+
matched = false;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if matched {
|
|
126
|
+
return Some((route.action.clone(), params));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
None
|
|
131
|
+
}
|