tangled
alpha
login
or
join now
socksthewolf.com
/
skyscheduler
1
fork
atom
Schedule posts to Bluesky with Cloudflare workers.
skyscheduler.work
cf
tool
bsky-tool
cloudflare
bluesky
schedule
bsky
service
social-media
cloudflare-workers
1
fork
atom
overview
issues
pulls
pipelines
fix up reset passwords
also just make it look better
SocksTheWolf
3 weeks ago
26e59ce9
644d168a
+15
-35
5 changed files
expand all
collapse all
unified
split
assets
js
reset.js
src
auth
index.ts
endpoints
openapi.tsx
index.tsx
validation
accountResetSchema.ts
+1
-1
assets/js/reset.js
···
1
function handleResetLoad() {
2
if (resetToken = new URLSearchParams(window.location.search).get("token")) {
3
const resetTokenField = document.getElementById("resetToken");
4
-
const submitButton = document.getElementById("submitButton");
5
if (resetTokenField && submitButton) {
6
resetTokenField.value = encodeURI(resetToken);
7
submitButton.removeAttribute("disabled");
···
1
function handleResetLoad() {
2
if (resetToken = new URLSearchParams(window.location.search).get("token")) {
3
const resetTokenField = document.getElementById("resetToken");
4
+
const submitButton = document.querySelector('button[type="submit"]');
5
if (resetTokenField && submitButton) {
6
resetTokenField.value = encodeURI(resetToken);
7
submitButton.removeAttribute("disabled");
+4
-4
src/auth/index.ts
···
5
import { drizzle, DrizzleD1Database } from "drizzle-orm/d1";
6
import { schema } from "../db";
7
import { BSKY_MAX_USERNAME_LENGTH, BSKY_MIN_USERNAME_LENGTH } from "../limits";
8
-
import { APP_NAME } from "../siteinfo";
9
import { Bindings } from "../types";
10
import { lookupBskyHandle } from "../utils/bskyApi";
11
import { createDMWithUser } from "../utils/bskyMsg";
12
13
-
function createPasswordResetMessage(url: string) {
14
return `Your ${APP_NAME} password reset url is:
15
-
${url}
16
17
This URL will expire in about an hour.
18
···
71
const userName = (user as any).username;
72
const bskyUserId = await lookupBskyHandle(userName);
73
if (bskyUserId !== null) {
74
-
const response = await createDMWithUser(env!, bskyUserId, createPasswordResetMessage(url));
75
if (!response)
76
throw new Error("FAILED_MESSAGE");
77
} else {
···
5
import { drizzle, DrizzleD1Database } from "drizzle-orm/d1";
6
import { schema } from "../db";
7
import { BSKY_MAX_USERNAME_LENGTH, BSKY_MIN_USERNAME_LENGTH } from "../limits";
8
+
import { APP_NAME, SITE_URL } from "../siteinfo";
9
import { Bindings } from "../types";
10
import { lookupBskyHandle } from "../utils/bskyApi";
11
import { createDMWithUser } from "../utils/bskyMsg";
12
13
+
function createPasswordResetMessage(url: string, token: string) {
14
return `Your ${APP_NAME} password reset url is:
15
+
${SITE_URL}/reset-password/${token}
16
17
This URL will expire in about an hour.
18
···
71
const userName = (user as any).username;
72
const bskyUserId = await lookupBskyHandle(userName);
73
if (bskyUserId !== null) {
74
+
const response = await createDMWithUser(env!, bskyUserId, createPasswordResetMessage(url, token));
75
if (!response)
76
throw new Error("FAILED_MESSAGE");
77
} else {
+1
-16
src/endpoints/openapi.tsx
···
5
import { ContextVariables } from "../auth";
6
import { Bindings } from "../types";
7
import { AccountDeleteSchema, AccountForgotSchema } from "../validation/accountForgotDeleteSchema";
8
-
import {
9
-
AccountResetSchema, PasswordResetCheckCallbackParam,
10
-
PasswordResetTokenParam
11
-
} from "../validation/accountResetSchema";
12
import { AccountUpdateSchema } from "../validation/accountUpdateSchema";
13
import { LoginSchema } from "../validation/loginSchema";
14
import { FileDeleteSchema } from "../validation/mediaSchema";
···
423
}
424
}
425
}), validator("param", CheckFileSchema));
426
-
427
-
openapiRoutes.get("/api/auth/reset-password/:id", describeRoute({
428
-
description: "resets a password",
429
-
responses: {
430
-
200: {
431
-
description: "valid token, redirect to reset"
432
-
},
433
-
404: {
434
-
description: "reset token is invalid"
435
-
}
436
-
}
437
-
}), validator("param", PasswordResetTokenParam), validator("query", PasswordResetCheckCallbackParam));
···
5
import { ContextVariables } from "../auth";
6
import { Bindings } from "../types";
7
import { AccountDeleteSchema, AccountForgotSchema } from "../validation/accountForgotDeleteSchema";
8
+
import { AccountResetSchema } from "../validation/accountResetSchema";
0
0
0
9
import { AccountUpdateSchema } from "../validation/accountUpdateSchema";
10
import { LoginSchema } from "../validation/loginSchema";
11
import { FileDeleteSchema } from "../validation/mediaSchema";
···
420
}
421
}
422
}), validator("param", CheckFileSchema));
0
0
0
0
0
0
0
0
0
0
0
0
+8
src/index.tsx
···
113
// Reset Password route
114
app.get("/reset", redirectToDashIfLogin, (c) => c.html(<ResetPassword />));
115
0
0
0
0
0
0
0
0
116
// Startup Application
117
app.get("/setup", async (c) => await setupAccounts(c));
118
···
113
// Reset Password route
114
app.get("/reset", redirectToDashIfLogin, (c) => c.html(<ResetPassword />));
115
116
+
// Reset Password Confirm route
117
+
app.get("/reset-password/:id", (c) => {
118
+
// Alternatively you can just URL rewrite this in cloudflare and it'll look
119
+
// 100x times better.
120
+
const { id } = c.req.param();
121
+
return c.redirect(`/api/auth/reset-password/${id}?callbackURL=%2Freset`);
122
+
});
123
+
124
// Startup Application
125
app.get("/setup", async (c) => await setupAccounts(c));
126
+1
-14
src/validation/accountResetSchema.ts
···
13
.max(MAX_DASHBOARD_PASS, "confirm password too long")
14
.nonempty("confirm password cannot be empty")
15
.nonoptional(),
16
-
}).refine((schema) => schema.confirmPassword === schema.password, "Passwords do not match");
17
-
18
-
// encoded strings
19
-
const uriComponent = z.codec(z.string(), z.string(), {
20
-
decode: (encodedString) => decodeURIComponent(encodedString),
21
-
encode: (decodedString) => encodeURIComponent(decodedString),
22
-
});
23
-
export const PasswordResetCheckCallbackParam = z.object({
24
-
callbackURL: z.literal(z.encode(uriComponent, "/reset"))
25
-
});
26
-
27
-
export const PasswordResetTokenParam = z.object({
28
-
id: z.string().min(20).max(64)
29
-
});
···
13
.max(MAX_DASHBOARD_PASS, "confirm password too long")
14
.nonempty("confirm password cannot be empty")
15
.nonoptional(),
16
+
}).refine((schema) => schema.confirmPassword === schema.password, "Passwords do not match");
0
0
0
0
0
0
0
0
0
0
0
0
0