@raketa-cloud/admin 0.0.1
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/biome.json +6 -0
- package/jest.config.js +15 -0
- package/package.json +31 -0
- package/src/app.tsx +19 -0
- package/src/crud/login.tsx +99 -0
- package/src/crud/logout.tsx +14 -0
- package/src/crud/tags.tsx +235 -0
- package/src/index.ts +1 -0
- package/src/lib/dxp/private/tags.ts +106 -0
- package/src/lib/dxp/protocol.ts +32 -0
- package/src/lib/gateway/login.ts +44 -0
- package/src/lib/gateway/protocol.ts +8 -0
- package/src/lib/gqlEngine.ts +17 -0
- package/src/lib/logger.tsx +149 -0
- package/src/middleware/authentication.ts +62 -0
- package/src/middleware/http_logger.ts +41 -0
- package/src/middleware/logger.ts +9 -0
- package/src/shared/Layout.tsx +50 -0
- package/tests/integration/authentication/middleware.test.ts +100 -0
- package/tests/integration/login/create.test.ts +117 -0
- package/tests/integration/login/index.test.ts +16 -0
- package/tests/integration/logout/delete.test.ts +17 -0
- package/tests/integration/tags/create.test.ts +66 -0
- package/tests/integration/tags/delete.test.ts +74 -0
- package/tests/integration/tags/edit.test.ts +43 -0
- package/tests/integration/tags/index.test.ts +66 -0
- package/tests/integration/tags/new.test.ts +16 -0
- package/tests/integration/tags/update.test.ts +87 -0
- package/tests/support/helpers.ts +53 -0
- package/tests/support/setup.js +9 -0
- package/tsconfig.json +15 -0
- package/turbo/generators/config.ts +30 -0
- package/turbo/generators/templates/component.hbs +14 -0
- package/turbo.json +9 -0
package/biome.json
ADDED
package/jest.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const { pathsToModuleNameMapper } = require("ts-jest");
|
|
2
|
+
const { compilerOptions } = require("./tsconfig.json");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
clearMocks: true,
|
|
6
|
+
preset: "ts-jest",
|
|
7
|
+
testEnvironment: "node",
|
|
8
|
+
globalSetup: "./tests/support/setup.js",
|
|
9
|
+
transform: {
|
|
10
|
+
"^.+\\.ts?$": "ts-jest",
|
|
11
|
+
},
|
|
12
|
+
roots: ["."],
|
|
13
|
+
modulePaths: [compilerOptions.baseUrl],
|
|
14
|
+
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths),
|
|
15
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@raketa-cloud/admin",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsup src/index.ts --format cjs --loader \".js=jsx\"",
|
|
9
|
+
"dev": "tsup src/index.ts --format cjs --loader \".js=jsx\" --watch",
|
|
10
|
+
"lint": "biome lint --apply .",
|
|
11
|
+
"test": "jest --verbose",
|
|
12
|
+
"format": "prettier --write ."
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@jest/globals": "^29.7.0",
|
|
16
|
+
"@repo/typescript-config": "*",
|
|
17
|
+
"@turbo/gen": "^1.11.3",
|
|
18
|
+
"@types/jest": "^29.5.8",
|
|
19
|
+
"@types/node": "^20.10.6",
|
|
20
|
+
"jest": "^29.7.0",
|
|
21
|
+
"ts-jest": "^29.1.1",
|
|
22
|
+
"tsup": "6.5.0",
|
|
23
|
+
"typescript": "^5.3.3"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@biomejs/biome": "1.5.3",
|
|
27
|
+
"graphql-request": "^6.1.0",
|
|
28
|
+
"hono": "^3.12.3",
|
|
29
|
+
"prettier": "^3.2.4"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/app.tsx
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import logger from "src/middleware/logger";
|
|
3
|
+
import httpLogger from "src/middleware/http_logger";
|
|
4
|
+
import auth from "src/middleware/authentication";
|
|
5
|
+
|
|
6
|
+
import tagsApp from "src/crud/tags";
|
|
7
|
+
import loginApp from "src/crud/login";
|
|
8
|
+
import logoutApp from "src/crud/logout";
|
|
9
|
+
|
|
10
|
+
const app = new Hono();
|
|
11
|
+
app.use("*", logger);
|
|
12
|
+
app.use("*", httpLogger);
|
|
13
|
+
app.use("*", auth);
|
|
14
|
+
|
|
15
|
+
app.route("/", tagsApp);
|
|
16
|
+
app.route("/", loginApp);
|
|
17
|
+
app.route("/", logoutApp);
|
|
18
|
+
|
|
19
|
+
export default app;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import type { FC } from "hono/jsx";
|
|
3
|
+
import Layout from "src/shared/Layout";
|
|
4
|
+
import login from "src/lib/gateway/login";
|
|
5
|
+
import { setCookie, deleteCookie } from "hono/cookie";
|
|
6
|
+
import { decode } from "hono/jwt";
|
|
7
|
+
import { CookieOptions } from "hono/utils/cookie";
|
|
8
|
+
|
|
9
|
+
const app = new Hono();
|
|
10
|
+
|
|
11
|
+
// Login part
|
|
12
|
+
const LoginForm: FC = ({ login = {}, errors = {} }) => {
|
|
13
|
+
return (
|
|
14
|
+
<div class="container mx-auto py-16">
|
|
15
|
+
<h1 class="mb-8 text-2xl font-bold">Login</h1>
|
|
16
|
+
|
|
17
|
+
{errors.general && (
|
|
18
|
+
<p>
|
|
19
|
+
<span>{errors.general}</span>
|
|
20
|
+
</p>
|
|
21
|
+
)}
|
|
22
|
+
|
|
23
|
+
<form method="POST" action="/login">
|
|
24
|
+
<div class="flex gap-2">
|
|
25
|
+
<label>Email</label>
|
|
26
|
+
<input type="email" value={login.email} name="email" class="border" />
|
|
27
|
+
{errors.email && <span>{errors.email}</span>}
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="flex gap-2">
|
|
31
|
+
<label>Password</label>
|
|
32
|
+
<input type="password" value="" name="password" class="border" />
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="flex gap-4">
|
|
36
|
+
<button type="submit">Login</button>
|
|
37
|
+
</div>
|
|
38
|
+
</form>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
app.get("/login", async (c) => {
|
|
44
|
+
const errors = {};
|
|
45
|
+
|
|
46
|
+
if (c.req.query().reason === "inactive") {
|
|
47
|
+
(errors as any).general =
|
|
48
|
+
"Your user is not activated. Please contact your administrator for more information";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return c.html(
|
|
52
|
+
<Layout context={c}>
|
|
53
|
+
<LoginForm errors={errors} />
|
|
54
|
+
</Layout>,
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
app.post("/login", async (c) => {
|
|
59
|
+
const body = await c.req.parseBody();
|
|
60
|
+
|
|
61
|
+
const email = body.email as string;
|
|
62
|
+
const password = body.password as string;
|
|
63
|
+
|
|
64
|
+
const resp = await login.create(email, password, c);
|
|
65
|
+
|
|
66
|
+
if ((resp as any).login.errors) {
|
|
67
|
+
const errors = {};
|
|
68
|
+
|
|
69
|
+
(resp as any).login.errors.forEach((error: any) => {
|
|
70
|
+
const key = error.key;
|
|
71
|
+
|
|
72
|
+
(errors as any)[key] = [(errors as any)[key] || "", error.message]
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join("; ");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
deleteCookie(c, "authToken");
|
|
78
|
+
return c.html(
|
|
79
|
+
<Layout context={c}>
|
|
80
|
+
<LoginForm login={{ email }} errors={errors} />
|
|
81
|
+
</Layout>,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const token = (resp as any).login.accessToken;
|
|
86
|
+
const expiration =
|
|
87
|
+
(decode(token) as any).payload.exp ||
|
|
88
|
+
(new Date().getTime() / 1000).toFixed(0);
|
|
89
|
+
|
|
90
|
+
const opts = {
|
|
91
|
+
expires: new Date(expiration * 1000),
|
|
92
|
+
} as CookieOptions;
|
|
93
|
+
|
|
94
|
+
setCookie(c, "authToken", token, opts);
|
|
95
|
+
|
|
96
|
+
return c.redirect("/tags", 301);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export default app;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { deleteCookie } from "hono/cookie";
|
|
3
|
+
|
|
4
|
+
const app = new Hono();
|
|
5
|
+
|
|
6
|
+
app.delete("/logout", async (c) => {
|
|
7
|
+
deleteCookie(c, "authToken");
|
|
8
|
+
|
|
9
|
+
c.header("HX-Redirect", "/login");
|
|
10
|
+
c.status(201);
|
|
11
|
+
return c.json({ message: "Bye" });
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default app;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { FC } from "hono/jsx";
|
|
2
|
+
import { Hono } from "hono";
|
|
3
|
+
import tags from "src/lib/dxp/private/tags";
|
|
4
|
+
import Layout from "src/shared/Layout";
|
|
5
|
+
|
|
6
|
+
const TagsList: FC = ({ data }) => {
|
|
7
|
+
// TODO: Pagination!
|
|
8
|
+
const list = data?.tags?.list || [];
|
|
9
|
+
|
|
10
|
+
const results = list.map((tag: any) => (
|
|
11
|
+
<tr class="bg-transparent hover:bg-yellow-50 transition">
|
|
12
|
+
<td class="p-4 border-b">
|
|
13
|
+
<a
|
|
14
|
+
href={`/tags/${tag.id}/edit`}
|
|
15
|
+
class="underline font-semibold hover:no-underline"
|
|
16
|
+
>
|
|
17
|
+
{tag.name}
|
|
18
|
+
</a>
|
|
19
|
+
</td>
|
|
20
|
+
<td class="p-4 border-b">{tag.slug}</td>
|
|
21
|
+
</tr>
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<>
|
|
26
|
+
{results.length > 0 && results}
|
|
27
|
+
{results.length === 0 && (
|
|
28
|
+
<tr>
|
|
29
|
+
<td colspan={2} class="p-4">
|
|
30
|
+
Not found.
|
|
31
|
+
</td>
|
|
32
|
+
</tr>
|
|
33
|
+
)}
|
|
34
|
+
</>
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const Tags: FC = ({ data }) => {
|
|
39
|
+
return (
|
|
40
|
+
<div class="container mx-auto py-16">
|
|
41
|
+
<h1 class="mb-8 text-2xl font-bold">Tags</h1>
|
|
42
|
+
|
|
43
|
+
<a href="/tags/new">New</a>
|
|
44
|
+
|
|
45
|
+
<table class="w-full">
|
|
46
|
+
<thead>
|
|
47
|
+
<tr>
|
|
48
|
+
<th class="p-4 border-b text-left">Name</th>
|
|
49
|
+
<th class="p-4 border-b text-left">URL</th>
|
|
50
|
+
</tr>
|
|
51
|
+
</thead>
|
|
52
|
+
<TagsList data={data} />
|
|
53
|
+
</table>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const Form: FC = ({ action, tag, errors = {} }) => {
|
|
59
|
+
return (
|
|
60
|
+
<form method="POST" action={action}>
|
|
61
|
+
<div class="flex gap-2">
|
|
62
|
+
<label>Name</label>
|
|
63
|
+
<input type="text" value={tag.name} name="name" class="border" />
|
|
64
|
+
{errors.name && <span>{errors.name}</span>}
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="flex gap-2">
|
|
68
|
+
<label>URL</label>
|
|
69
|
+
<input type="text" value={tag.slug} name="slug" class="border" />
|
|
70
|
+
{errors.slug && <span>{errors.slug}</span>}
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div class="flex gap-4">
|
|
74
|
+
<button type="submit">Save</button>
|
|
75
|
+
<a href="/tags">Back</a>
|
|
76
|
+
{tag.id && (
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
hx-delete={`/tags/${tag.id}`}
|
|
80
|
+
hx-confirm="Are you sure you wish to delete this tag?"
|
|
81
|
+
>
|
|
82
|
+
DELETE
|
|
83
|
+
</button>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</form>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const EditForm: FC = ({ tag, errors = {} }) => {
|
|
91
|
+
return (
|
|
92
|
+
<div class="container mx-auto py-16">
|
|
93
|
+
<h1 class="mb-8 text-2xl font-bold">Tags</h1>
|
|
94
|
+
|
|
95
|
+
<Form action={`/tags/${tag.id}`} tag={tag} errors={errors} />
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const NewForm: FC = ({ tag = {}, errors = {} }) => {
|
|
101
|
+
return (
|
|
102
|
+
<div class="container mx-auto py-16">
|
|
103
|
+
<h1 class="mb-8 text-2xl font-bold">Tags</h1>
|
|
104
|
+
|
|
105
|
+
<Form action="/tags" tag={tag} errors={errors} />
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const app = new Hono();
|
|
111
|
+
|
|
112
|
+
app.get("/tags", async (c) => {
|
|
113
|
+
const data = await tags.list({ input: { locale: "en" } }, c);
|
|
114
|
+
|
|
115
|
+
return c.html(
|
|
116
|
+
<Layout context={c}>
|
|
117
|
+
<Tags data={data} />
|
|
118
|
+
</Layout>,
|
|
119
|
+
);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
app.get("/tags/new", (c) => c.html(<NewForm />));
|
|
123
|
+
|
|
124
|
+
app.get("/tags/:id/edit", async (c) => {
|
|
125
|
+
const { tag }: any = await tags.find(parseInt(c.req.param().id, 10), "en", c);
|
|
126
|
+
|
|
127
|
+
return c.html(
|
|
128
|
+
<Layout context={c}>
|
|
129
|
+
<EditForm tag={tag} />
|
|
130
|
+
</Layout>,
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
app.delete("/tags/:id", async (c) => {
|
|
135
|
+
const { tag }: any = await tags.find(parseInt(c.req.param().id, 10), "en", c);
|
|
136
|
+
const resp = await tags.delete(tag.id, "en", c);
|
|
137
|
+
const deleteTag = (resp as any).deleteTag;
|
|
138
|
+
|
|
139
|
+
if (deleteTag.errors) {
|
|
140
|
+
throw `ERROR: ${deleteTag.errors}`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
c.header("HX-Redirect", "/tags");
|
|
144
|
+
c.status(201);
|
|
145
|
+
|
|
146
|
+
return c.json(deleteTag.tag);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
app.post("/tags/:id", async (c) => {
|
|
150
|
+
const body = await c.req.parseBody();
|
|
151
|
+
|
|
152
|
+
const { tag }: any = await tags.find(parseInt(c.req.param().id, 10), "en", c);
|
|
153
|
+
|
|
154
|
+
const resp = await tags.update(
|
|
155
|
+
{
|
|
156
|
+
input: {
|
|
157
|
+
locale: "en",
|
|
158
|
+
id: tag.id,
|
|
159
|
+
tag: {
|
|
160
|
+
name: body.name as string,
|
|
161
|
+
slug: body.slug as string,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
c,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if ((resp as any).updateTag.errors) {
|
|
169
|
+
const errors = {};
|
|
170
|
+
|
|
171
|
+
(resp as any).updateTag.errors.forEach((error: any) => {
|
|
172
|
+
const key = error.key;
|
|
173
|
+
|
|
174
|
+
(errors as any)[key] = [(errors as any)[key] || "", error.message]
|
|
175
|
+
.filter(Boolean)
|
|
176
|
+
.join("; ");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return c.html(
|
|
180
|
+
<Layout context={c}>
|
|
181
|
+
<EditForm
|
|
182
|
+
tag={{
|
|
183
|
+
id: tag.id,
|
|
184
|
+
name: body.name as string,
|
|
185
|
+
slug: body.slug as string,
|
|
186
|
+
}}
|
|
187
|
+
errors={errors}
|
|
188
|
+
/>
|
|
189
|
+
</Layout>,
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return c.redirect("/tags", 301);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
app.post("/tags", async (c) => {
|
|
197
|
+
const body = await c.req.parseBody();
|
|
198
|
+
|
|
199
|
+
const tag = {
|
|
200
|
+
name: body.name as string,
|
|
201
|
+
slug: body.slug as string,
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const resp = await tags.create(
|
|
205
|
+
{
|
|
206
|
+
input: {
|
|
207
|
+
locale: "en",
|
|
208
|
+
tag,
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
c,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if ((resp as any).createTag.errors) {
|
|
215
|
+
const errors = {};
|
|
216
|
+
|
|
217
|
+
(resp as any).createTag.errors.forEach((error: any) => {
|
|
218
|
+
const key = error.key;
|
|
219
|
+
|
|
220
|
+
(errors as any)[key] = [(errors as any)[key] || "", error.message]
|
|
221
|
+
.filter(Boolean)
|
|
222
|
+
.join("; ");
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
return c.html(
|
|
226
|
+
<Layout context={c}>
|
|
227
|
+
<NewForm tag={tag} errors={errors} />
|
|
228
|
+
</Layout>,
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return c.redirect("/tags", 301);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
export default app;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as adminApp } from "./app";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Context } from "hono";
|
|
2
|
+
import { dxpPrivateRequest } from "../protocol";
|
|
3
|
+
import { gql } from "../../gqlEngine";
|
|
4
|
+
|
|
5
|
+
const TagsDocument = gql`
|
|
6
|
+
query Tags($input: TagsInput!) {
|
|
7
|
+
tags(input: $input) {
|
|
8
|
+
list {
|
|
9
|
+
id
|
|
10
|
+
name
|
|
11
|
+
slug
|
|
12
|
+
}
|
|
13
|
+
pagination {
|
|
14
|
+
currentPage
|
|
15
|
+
pagesCount
|
|
16
|
+
entriesCount
|
|
17
|
+
pageSize
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
|
|
23
|
+
const CreateTagDocument = gql`
|
|
24
|
+
mutation CreateTag($input: CreateTagInput!) {
|
|
25
|
+
createTag(input: $input) {
|
|
26
|
+
tag {
|
|
27
|
+
id
|
|
28
|
+
slug
|
|
29
|
+
name
|
|
30
|
+
}
|
|
31
|
+
errors {
|
|
32
|
+
key
|
|
33
|
+
message
|
|
34
|
+
codes {
|
|
35
|
+
key
|
|
36
|
+
placeholders
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const UpdateTagDocument = gql`
|
|
44
|
+
mutation UpdateTag($input: UpdateTagInput!) {
|
|
45
|
+
updateTag(input: $input) {
|
|
46
|
+
tag {
|
|
47
|
+
id
|
|
48
|
+
slug
|
|
49
|
+
name
|
|
50
|
+
}
|
|
51
|
+
errors {
|
|
52
|
+
key
|
|
53
|
+
message
|
|
54
|
+
codes {
|
|
55
|
+
key
|
|
56
|
+
placeholders
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const DeleteTagDocument = gql`
|
|
64
|
+
mutation DeleteTag($input: IdInput!) {
|
|
65
|
+
deleteTag(input: $input) {
|
|
66
|
+
tag {
|
|
67
|
+
id
|
|
68
|
+
slug
|
|
69
|
+
name
|
|
70
|
+
}
|
|
71
|
+
errors {
|
|
72
|
+
key
|
|
73
|
+
message
|
|
74
|
+
codes {
|
|
75
|
+
key
|
|
76
|
+
placeholders
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
const TagByIdDocument = gql`
|
|
84
|
+
query TagByID($input: IdInput!) {
|
|
85
|
+
tag(input: $input) {
|
|
86
|
+
id
|
|
87
|
+
slug
|
|
88
|
+
name
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
`;
|
|
92
|
+
|
|
93
|
+
const tags = {
|
|
94
|
+
list: async (variables: any, honoCtx: Context) =>
|
|
95
|
+
dxpPrivateRequest(TagsDocument, variables, honoCtx),
|
|
96
|
+
find: async (id: number, locale: string, honoCtx: Context) =>
|
|
97
|
+
dxpPrivateRequest(TagByIdDocument, { input: { id, locale } }, honoCtx),
|
|
98
|
+
create: async (variables: any, honoCtx: Context) =>
|
|
99
|
+
dxpPrivateRequest(CreateTagDocument, variables, honoCtx),
|
|
100
|
+
update: async (variables: any, honoCtx: Context) =>
|
|
101
|
+
dxpPrivateRequest(UpdateTagDocument, variables, honoCtx),
|
|
102
|
+
delete: async (id: number, locale: string, honoCtx: Context) =>
|
|
103
|
+
dxpPrivateRequest(DeleteTagDocument, { input: { id, locale } }, honoCtx),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export default tags;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getCookie } from "hono/cookie";
|
|
2
|
+
import { env } from "hono/adapter";
|
|
3
|
+
import { gqlRequest } from "../gqlEngine";
|
|
4
|
+
|
|
5
|
+
export function dxpPublicRequest(query: string, variables: any, honoCtx: any) {
|
|
6
|
+
const {
|
|
7
|
+
RAKETA_GATEWAY_DISPATCH_URL,
|
|
8
|
+
RAKETA_GATEWAY_PROJECT,
|
|
9
|
+
RAKETA_DXP_PRIVATE_SERVICE,
|
|
10
|
+
} = env(honoCtx);
|
|
11
|
+
|
|
12
|
+
return gqlRequest(RAKETA_GATEWAY_DISPATCH_URL, query, variables, {
|
|
13
|
+
"x-raketa-project": RAKETA_GATEWAY_PROJECT,
|
|
14
|
+
"x-raketa-service": RAKETA_DXP_PRIVATE_SERVICE,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function dxpPrivateRequest(query: string, variables: any, honoCtx: any) {
|
|
19
|
+
const token = getCookie(honoCtx, "authToken");
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
RAKETA_GATEWAY_DISPATCH_URL,
|
|
23
|
+
RAKETA_GATEWAY_PROJECT,
|
|
24
|
+
RAKETA_DXP_PRIVATE_SERVICE,
|
|
25
|
+
} = env(honoCtx);
|
|
26
|
+
|
|
27
|
+
return gqlRequest(RAKETA_GATEWAY_DISPATCH_URL, query, variables, {
|
|
28
|
+
"x-raketa-project": RAKETA_GATEWAY_PROJECT,
|
|
29
|
+
"x-raketa-service": RAKETA_DXP_PRIVATE_SERVICE,
|
|
30
|
+
"x-raketa-identity": token,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Context } from "hono";
|
|
2
|
+
import { env } from "hono/adapter";
|
|
3
|
+
import { gql } from "../gqlEngine";
|
|
4
|
+
import { gatewayRequest } from "./protocol";
|
|
5
|
+
|
|
6
|
+
const LoginDocument = gql`
|
|
7
|
+
mutation Login($input: LoginInput!) {
|
|
8
|
+
login(input: $input) {
|
|
9
|
+
accessToken
|
|
10
|
+
errors {
|
|
11
|
+
key
|
|
12
|
+
message
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const login = {
|
|
19
|
+
create: async (email: string, password: string, honoCtx: Context) => {
|
|
20
|
+
const {
|
|
21
|
+
RAKETA_GATEWAY_PRIMARY_USER_POOL,
|
|
22
|
+
RAKETA_GATEWAY_SECONDARY_USER_POOL,
|
|
23
|
+
RAKETA_GATEWAY_PROJECT,
|
|
24
|
+
} = env(honoCtx);
|
|
25
|
+
|
|
26
|
+
return gatewayRequest(
|
|
27
|
+
LoginDocument,
|
|
28
|
+
{
|
|
29
|
+
input: {
|
|
30
|
+
email,
|
|
31
|
+
password,
|
|
32
|
+
userPools: [
|
|
33
|
+
RAKETA_GATEWAY_PRIMARY_USER_POOL,
|
|
34
|
+
RAKETA_GATEWAY_SECONDARY_USER_POOL,
|
|
35
|
+
].filter(Boolean),
|
|
36
|
+
project: RAKETA_GATEWAY_PROJECT,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
honoCtx,
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export default login;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { env } from "hono/adapter";
|
|
2
|
+
import { gqlRequest } from "../gqlEngine";
|
|
3
|
+
|
|
4
|
+
export function gatewayRequest(query: string, variables: any, honoCtx: any) {
|
|
5
|
+
const { RAKETA_GATEWAY_AUTH_URL } = env(honoCtx);
|
|
6
|
+
|
|
7
|
+
return gqlRequest(RAKETA_GATEWAY_AUTH_URL, query, variables);
|
|
8
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { request, gql } from "graphql-request";
|
|
2
|
+
|
|
3
|
+
export function gqlRequest(
|
|
4
|
+
url: string,
|
|
5
|
+
query: string,
|
|
6
|
+
variables = {},
|
|
7
|
+
headers = {},
|
|
8
|
+
) {
|
|
9
|
+
return request({
|
|
10
|
+
url,
|
|
11
|
+
document: query,
|
|
12
|
+
variables: variables,
|
|
13
|
+
requestHeaders: headers,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { gql };
|