Working on auth...
This commit is contained in:
parent
7a5f83c633
commit
f0755615ae
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/config.json
|
|
@ -9,10 +9,10 @@ interface TableSpec {
|
|||
prepStatements?: string[];
|
||||
}
|
||||
|
||||
const timestamps = [
|
||||
"created_at timestamptz not null default now()",
|
||||
"updated_at timestamptz not null default now()",
|
||||
];
|
||||
const createdAtTimestamp = "created_at timestamptz not null default now()";
|
||||
const updatedAtTimestamp = "updated_at timestamptz not null default now()";
|
||||
|
||||
const timestamps = [createdAtTimestamp, updatedAtTimestamp];
|
||||
|
||||
const tables: Record<string, TableSpec> = {
|
||||
"note": {
|
||||
|
@ -26,15 +26,30 @@ const tables: Record<string, TableSpec> = {
|
|||
columns: [
|
||||
id,
|
||||
"username text not null unique",
|
||||
"hashed_password text not null",
|
||||
"password_digest text not null",
|
||||
"status user_status not null",
|
||||
"display_name text",
|
||||
...timestamps,
|
||||
],
|
||||
additionalTableStatements: [
|
||||
"constraint valid_username check (username ~* '^[a-z\d\\-_]{2,38}$')",
|
||||
],
|
||||
},
|
||||
"user_token": {
|
||||
prepStatements: [
|
||||
"drop type if exists user_token_type",
|
||||
"create type user_token_type as enum ('session', 'reset')",
|
||||
],
|
||||
columns: [
|
||||
id,
|
||||
"token_digest bytea not null unique",
|
||||
"type user_token_type not null",
|
||||
"sent_to text not null",
|
||||
createdAtTimestamp,
|
||||
],
|
||||
additionalStatements: [
|
||||
"create index team_user_type on user_token (type)",
|
||||
"create index team_user_sent_to on user_token (sent_to)",
|
||||
],
|
||||
},
|
||||
"team": {
|
||||
|
@ -94,8 +109,20 @@ create extension if not exists "uuid-ossp";
|
|||
|
||||
${createTables}
|
||||
|
||||
commit;
|
||||
`;
|
||||
|
||||
const setupResult = await query(queryString);
|
||||
|
||||
console.debug(setupResult);
|
||||
console.log(queryString);
|
||||
|
||||
const seedQuery = `
|
||||
|
||||
-- TODO: create reserved usernames?
|
||||
|
||||
with new_user as (
|
||||
insert into "user" (username, hashed_password, status)
|
||||
insert into "user" (username, password_digest, status)
|
||||
values ('lytedev', '$2a$10$9fyDAOz6H4a393KHyjbvIe1WFxbhCJhq/CZmlXcEg4d1bE9Ey25WW', 'superadmin')
|
||||
returning id as user_id
|
||||
), new_team as (
|
||||
|
@ -108,14 +135,11 @@ insert into "team_user" (user_id, team_id, status)
|
|||
(select user_id from new_user),
|
||||
(select team_id from new_team),
|
||||
'owner'
|
||||
);
|
||||
) returning user_id;
|
||||
|
||||
commit;
|
||||
`;
|
||||
|
||||
console.log(queryString);
|
||||
const seedResult = await query(seedQuery);
|
||||
|
||||
const result = await query(queryString);
|
||||
|
||||
console.debug(result);
|
||||
console.log(queryString);
|
||||
console.debug(seedResult);
|
||||
console.log(seedQuery);
|
||||
|
|
39
mail/mod.ts
Normal file
39
mail/mod.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import Mailgun from "https://deno.land/x/mailgun@v1.1.1/index.ts";
|
||||
import { type Message } from "https://deno.land/x/mailgun@v1.1.1/types.ts";
|
||||
|
||||
export type Email = Message;
|
||||
|
||||
const MAILGUN_API_KEY = Deno.env.get("MAILGUN_API_KEY");
|
||||
const MAILGUN_DOMAIN = Deno.env.get("MAILGUN_DOMAIN");
|
||||
|
||||
if (!MAILGUN_API_KEY) {
|
||||
console.error(
|
||||
"MAILGUN_API_KEY not set. Emails will not be sent, only logged to the console.",
|
||||
);
|
||||
}
|
||||
|
||||
if (!MAILGUN_DOMAIN) {
|
||||
console.error(
|
||||
"MAILGUN_API_KEY not set. Emails will not be sent, only logged to the console.",
|
||||
);
|
||||
}
|
||||
|
||||
const mailgun = new Mailgun({
|
||||
key: "YOUR_API_KEY",
|
||||
region: "us",
|
||||
domain: "YOUR_DOMAIN",
|
||||
});
|
||||
|
||||
export async function send(email: Email) {
|
||||
console.debug("Email:", email);
|
||||
if (MAILGUN_API_KEY && MAILGUN_DOMAIN) {
|
||||
return await mailgun.send(email);
|
||||
} else {
|
||||
return new Response(
|
||||
"Email client is not properly configured. Email not sent.",
|
||||
{
|
||||
status: 401,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ export const handler: Handlers<UserID | LoginError | null> = {
|
|||
}
|
||||
|
||||
const result = await query<
|
||||
{ id: string; username: string; hashed_password: string }
|
||||
{ id: string; username: string; password_digest: string }
|
||||
>(
|
||||
`select * from "user" where username = $1`,
|
||||
[username],
|
||||
|
@ -34,8 +34,8 @@ export const handler: Handlers<UserID | LoginError | null> = {
|
|||
if (result == null || result.rows.length < 1) {
|
||||
return await invalidLogin(context);
|
||||
}
|
||||
const { rows: [{ id, hashed_password }] } = result;
|
||||
if (await compare(password.toString(), hashed_password)) {
|
||||
const { rows: [{ id, password_digest }] } = result;
|
||||
if (await compare(password.toString(), password_digest)) {
|
||||
return await context.render(id);
|
||||
} else {
|
||||
return await invalidLogin(context);
|
||||
|
|
|
@ -21,13 +21,13 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
|
|||
if (!password) {
|
||||
return await context.render({ message: "no password provided" });
|
||||
}
|
||||
const hashed_password = await hash(password.toString());
|
||||
const password_digest = await hash(password.toString());
|
||||
try {
|
||||
const result = await query<{ user_id: string }>(
|
||||
`
|
||||
with new_user as (
|
||||
insert into "user" (username, hashed_password, status)
|
||||
values ($username, $hashed_password, 'unverified')
|
||||
insert into "user" (username, password_digest, status)
|
||||
values ($username, $password_digest, 'unverified')
|
||||
returning id as user_id
|
||||
), new_team as (
|
||||
insert into "team" (display_name)
|
||||
|
@ -41,13 +41,14 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
|
|||
'owner'
|
||||
) returning user_id
|
||||
`,
|
||||
{ username, hashed_password, team_name: `${username}'s First Team` },
|
||||
{ username, password_digest, team_name: `${username}'s First Team` },
|
||||
);
|
||||
console.debug(result);
|
||||
if (!result) throw "insert failed";
|
||||
const { rows: [{ user_id: id }] } = result;
|
||||
return await context.render(id);
|
||||
} catch (err) {
|
||||
console.log("Error fields:", { ...err });
|
||||
if (
|
||||
err instanceof PostgresError && err.fields.code == "23505" &&
|
||||
err.fields.constraint == "user_username_key"
|
||||
|
@ -55,6 +56,14 @@ export const handler: Handlers<UserID | RegistrationError | null> = {
|
|||
return await context.render({
|
||||
message: `A user with username '${username}' already exists`,
|
||||
});
|
||||
} else if (
|
||||
err instanceof PostgresError && err.fields.code == "23514" &&
|
||||
err.fields.constraint == "valid_username"
|
||||
) {
|
||||
return await context.render({
|
||||
message:
|
||||
`Username must ONLY be comprised of letters, number, dashes, and underscores`,
|
||||
});
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue