mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-08-22 16:47:16 -05:00
feat: Add register + hcaptcha
This commit is contained in:
22
README.md
22
README.md
@@ -21,16 +21,24 @@ If you'd like to help localize Pretendo Network, you can contribute to the trans
|
||||
# Website refactor
|
||||
|
||||
The website is currently in a refactor, here is what is still left on the frontend:
|
||||
- [ ] Term pages (nuxt content)
|
||||
- [ ] Docs (nuxt content)
|
||||
- [ ] Styling for progress page
|
||||
- [ ] Styling for progress on main page
|
||||
- [ ] Donation tier page
|
||||
- [ ] Mii editor
|
||||
- [ ] Account page
|
||||
- [ ] Account: Mii editor
|
||||
- [ ] Account: View account
|
||||
- [ ] Account: Delete account
|
||||
- [ ] Account: Link discord account
|
||||
- [ ] Account: Edit server environment
|
||||
- [ ] Donations: stripe checkout frontend
|
||||
- [ ] Auth: Login
|
||||
- [ ] Auth: Logout
|
||||
- [ ] Auth: Register
|
||||
- [ ] Auth: Forgot password flow
|
||||
- [ ] Content: Term pages
|
||||
- [ ] Content: Documentation pages
|
||||
- [x] Content: Blog
|
||||
|
||||
And the tasks left on the backend:
|
||||
- [ ] Registration (with captchas)
|
||||
- [x] Registration (with captchas)
|
||||
- [x] Logout
|
||||
- [x] Login
|
||||
- [x] Password forgot flow
|
||||
@@ -41,6 +49,8 @@ And the tasks left on the backend:
|
||||
- [x] Account editing (mii saving, server environment changes)
|
||||
- [x] Delete account
|
||||
- [x] Discourse SSO
|
||||
- [x] HCaptcha support
|
||||
- [x] RSS feed
|
||||
|
||||
Miscellanous tasks:
|
||||
- [ ] Merge upstream changes into refactor branch
|
||||
|
||||
@@ -34,6 +34,8 @@ export default defineNuxtConfig({
|
||||
githubApiToken: '',
|
||||
stripeSecretKey: '',
|
||||
stripeNotificationEmail: '',
|
||||
hcaptchaSiteKey: '',
|
||||
hcaptchaSecretKey: '',
|
||||
grpcHost: '',
|
||||
grpcApiKey: '',
|
||||
mongoConnectionString: '',
|
||||
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@@ -22,6 +22,7 @@
|
||||
"discord-api-types": "^0.38.53",
|
||||
"eslint": "^9.39.5",
|
||||
"feed": "^6.0.0",
|
||||
"hcaptcha": "^0.2.0",
|
||||
"mongodb": "^7.5.0",
|
||||
"nice-grpc": "^2.1.17",
|
||||
"nodemailer": "^9.0.5",
|
||||
@@ -12651,6 +12652,12 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/hcaptcha": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hcaptcha/-/hcaptcha-0.2.0.tgz",
|
||||
"integrity": "sha512-x25z3RoEa9oqfyuQsgk2olc+LCNVDAJaGKUP1qFhpAybB6qjqOf4qB2y1E3LJpXDvM229JWEywc6iWnzWvGjNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/hookable": {
|
||||
"version": "5.5.3",
|
||||
"resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"discord-api-types": "^0.38.53",
|
||||
"eslint": "^9.39.5",
|
||||
"feed": "^6.0.0",
|
||||
"hcaptcha": "^0.2.0",
|
||||
"mongodb": "^7.5.0",
|
||||
"nice-grpc": "^2.1.17",
|
||||
"nodemailer": "^9.0.5",
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import { hcaptchaVerify } from "~~/server/utils/hcaptcha";
|
||||
import { ForgotPasswordSchema } from "~~/shared/api-types";
|
||||
|
||||
export default defineEventHandler(async (event): Promise<void> => {
|
||||
const body = await readZodBody(event, ForgotPasswordSchema);
|
||||
const grpc = useApiGrpc(event);
|
||||
|
||||
const captchaResult = await hcaptchaVerify(event, body.captchaResponse);
|
||||
if (!captchaResult) throw createError({
|
||||
status: 400,
|
||||
message: 'Invalid captcha',
|
||||
});
|
||||
|
||||
await grpc.forgotPassword({
|
||||
emailAddressOrUsername: body.emailOrPassword,
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useLegacyApiGrpc } from "~~/server/utils/useGrpc";
|
||||
|
||||
export default defineEventHandler(async (event): Promise<ApiAuthLogin> => {
|
||||
const body = await readZodBody(event, LoginSchema);
|
||||
const grpc = useLegacyApiGrpc(event);
|
||||
const grpc = useApiGrpc(event);
|
||||
|
||||
try {
|
||||
const res = await grpc.login({
|
||||
|
||||
24
server/api/auth/register.post.ts
Normal file
24
server/api/auth/register.post.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ApiAuthLogin, RegisterSchema } from "#shared/api-types"
|
||||
|
||||
export default defineEventHandler(async (event): Promise<ApiAuthLogin> => {
|
||||
const body = await readZodBody(event, RegisterSchema);
|
||||
const grpc = useApiGrpc(event);
|
||||
|
||||
try {
|
||||
const res = await grpc.register({
|
||||
email: body.email,
|
||||
miiName: body.miiName,
|
||||
captchaResponse: body.captchaResponse,
|
||||
username: body.username,
|
||||
password: body.password,
|
||||
passwordConfirm: body.password
|
||||
});
|
||||
|
||||
return {
|
||||
accessToken: res.accessToken,
|
||||
refreshToken: res.refreshToken
|
||||
};
|
||||
} catch (error: unknown) {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
14
server/utils/hcaptcha.ts
Normal file
14
server/utils/hcaptcha.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { H3Event } from 'h3'
|
||||
import hcaptcha from 'hcaptcha'
|
||||
|
||||
export async function hcaptchaVerify(event: H3Event, captchaResponse: string | null | undefined): Promise<boolean> {
|
||||
const config = useRuntimeConfig(event);
|
||||
if (!config.hcaptchaSiteKey) return true; // No captcha is configured, always valid
|
||||
if (!config.hcaptchaSecretKey) throw new Error("Hcaptcha not configured correctly, missing secret key");
|
||||
|
||||
if (!captchaResponse) return false; // No captcha filled in, invalid
|
||||
const captchaVerify = await hcaptcha.verify(config.hcaptchaSiteKey, captchaResponse, undefined, config.hcaptchaSiteKey);
|
||||
|
||||
if (!captchaVerify.success) return false; // Invalid captcha response
|
||||
return true;
|
||||
}
|
||||
@@ -64,6 +64,15 @@ export const LoginSchema = z.object({
|
||||
});
|
||||
export type ApiAuthLoginRequest = z.infer<typeof LoginSchema>;
|
||||
|
||||
export const RegisterSchema = z.object({
|
||||
email: z.email(),
|
||||
username: z.string(),
|
||||
miiName: z.string(),
|
||||
password: z.string(),
|
||||
captchaResponse: z.string().optional()
|
||||
});
|
||||
export type ApiAuthRegisterRequest = z.infer<typeof RegisterSchema>;
|
||||
|
||||
export const AccountUpdateSchema = z.object({
|
||||
mii: z.object({
|
||||
name: z.string(),
|
||||
@@ -83,6 +92,7 @@ export type ApiAuthResetPasswordRequest = z.infer<typeof ResetPasswordSchema>;
|
||||
|
||||
export const ForgotPasswordSchema = z.object({
|
||||
emailOrPassword: z.string(),
|
||||
captchaResponse: z.string().optional()
|
||||
});
|
||||
export type ApiAuthForgotPasswordRequest = z.infer<typeof ForgotPasswordSchema>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user