fix: get branch full working
This commit is contained in:
@@ -49,7 +49,6 @@
|
||||
"@sentry/profiling-node": "^7.110.1",
|
||||
"@supabase/supabase-js": "^2.32.0",
|
||||
"axios": "^1.4.0",
|
||||
"bcrypt": "^6.0.0",
|
||||
"dotenv": "^16.3.1",
|
||||
"hono": "^4.9.7",
|
||||
"hono-openapi": "^1.0.8",
|
||||
@@ -62,7 +61,6 @@
|
||||
"zod-openapi": "^5.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^6.0.0",
|
||||
"@types/katex": "^0.16.7",
|
||||
"@types/node": "^20.19.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.0.1",
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import { rateLimiter } from '@api/utils/redis/limiter';
|
||||
import { userKeyGenerator } from '../utils/redis/key-generators';
|
||||
import { env } from '@api/config/env';
|
||||
|
||||
export interface RateLimiterOptions {
|
||||
windowMs?: number;
|
||||
@@ -29,7 +30,13 @@ export const createRateLimiter = (options: RateLimiterOptions = {}): MiddlewareH
|
||||
const opts = { ...defaultOptions, ...options };
|
||||
|
||||
return async (c, next) => {
|
||||
// Skip rate limiting if not in production
|
||||
if (env.NODE_ENV !== 'production') {
|
||||
return await next();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// Generate rate limit key
|
||||
const key = opts.keyGenerator(c);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '@cio/ui/base/button';
|
||||
import { MonitorIcon, SunIcon, MoonIcon } from '@lucide/svelte';
|
||||
import { userPrefersMode, resetMode, setMode } from 'mode-watcher';
|
||||
import { userPrefersMode, resetMode, setMode } from '@cio/ui/base/dark-mode';
|
||||
|
||||
type UserPrefersMode = typeof userPrefersMode.current;
|
||||
|
||||
|
||||
@@ -21,12 +21,14 @@
|
||||
"@cio/email": "workspace:*",
|
||||
"@cio/tsconfig": "workspace:*",
|
||||
"@cio/utils": "workspace:*",
|
||||
"bcrypt": "6.0.0",
|
||||
"better-auth": "^1.4.1",
|
||||
"drizzle-kit": "^0.31.5",
|
||||
"drizzle-orm": "^0.44.7",
|
||||
"postgres": "^3.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "6.0.0",
|
||||
"@types/node": "^24.8.1",
|
||||
"dotenv": "^17.2.3",
|
||||
"tsc-alias": "^1.8.16",
|
||||
|
||||
@@ -1,173 +1,177 @@
|
||||
import 'dotenv/config';
|
||||
/**
|
||||
* Commenting this script out.
|
||||
*/
|
||||
|
||||
import { admin, anonymous } from 'better-auth/plugins';
|
||||
// import 'dotenv/config';
|
||||
|
||||
import { Pool } from 'pg';
|
||||
import { User as SupabaseUser } from '@supabase/supabase-js';
|
||||
import { betterAuth } from 'better-auth';
|
||||
// import { admin, anonymous } from 'better-auth/plugins';
|
||||
|
||||
// We create a custom betterAuth here because this code works well with pg
|
||||
const auth = betterAuth({
|
||||
database: new Pool({
|
||||
connectionString: process.env.DATABASE_URL
|
||||
}),
|
||||
emailVerification: {},
|
||||
emailAndPassword: {
|
||||
enabled: true
|
||||
},
|
||||
// import { Pool } from 'pg';
|
||||
// import { User as SupabaseUser } from '@supabase/supabase-js';
|
||||
// import { betterAuth } from 'better-auth';
|
||||
|
||||
socialProviders: {
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET!
|
||||
}
|
||||
},
|
||||
plugins: [admin(), anonymous()],
|
||||
// // We create a custom betterAuth here because this code works well with pg
|
||||
// const auth = betterAuth({
|
||||
// database: new Pool({
|
||||
// connectionString: process.env.DATABASE_URL
|
||||
// }),
|
||||
// emailVerification: {},
|
||||
// emailAndPassword: {
|
||||
// enabled: true
|
||||
// },
|
||||
|
||||
user: {
|
||||
modelName: 'user',
|
||||
fields: {
|
||||
emailVerified: 'email_verified',
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at'
|
||||
}
|
||||
},
|
||||
account: {
|
||||
modelName: 'account',
|
||||
fields: {
|
||||
userId: 'user_id',
|
||||
createdAt: 'created_at',
|
||||
updatedAt: 'updated_at',
|
||||
accountId: 'account_id',
|
||||
providerId: 'provider_id'
|
||||
}
|
||||
}
|
||||
});
|
||||
// socialProviders: {
|
||||
// google: {
|
||||
// clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||
// clientSecret: process.env.GOOGLE_CLIENT_SECRET!
|
||||
// }
|
||||
// },
|
||||
// plugins: [admin(), anonymous()],
|
||||
|
||||
type User = SupabaseUser & {
|
||||
is_super_admin: boolean;
|
||||
raw_user_meta_data: {
|
||||
avatar_url: string;
|
||||
};
|
||||
encrypted_password: string;
|
||||
email_confirmed_at: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
is_anonymous: boolean;
|
||||
profile_fullname: string;
|
||||
identities: {
|
||||
provider: string;
|
||||
identity_data: {
|
||||
sub: string;
|
||||
email: string;
|
||||
};
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
};
|
||||
// user: {
|
||||
// modelName: 'user',
|
||||
// fields: {
|
||||
// emailVerified: 'email_verified',
|
||||
// createdAt: 'created_at',
|
||||
// updatedAt: 'updated_at'
|
||||
// }
|
||||
// },
|
||||
// account: {
|
||||
// modelName: 'account',
|
||||
// fields: {
|
||||
// userId: 'user_id',
|
||||
// createdAt: 'created_at',
|
||||
// updatedAt: 'updated_at',
|
||||
// accountId: 'account_id',
|
||||
// providerId: 'provider_id'
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
const migrateFromSupabase = async () => {
|
||||
const ctx = await auth.$context;
|
||||
const db = ctx.options.database as Pool;
|
||||
const users = await db
|
||||
.query(
|
||||
`
|
||||
SELECT
|
||||
u.*,
|
||||
COALESCE(
|
||||
json_agg(
|
||||
i.* ORDER BY i.id
|
||||
) FILTER (WHERE i.id IS NOT NULL),
|
||||
'[]'::json
|
||||
) as identities,
|
||||
p.fullname as profile_fullname -- Select fullname from public.profile
|
||||
FROM auth.users u
|
||||
LEFT JOIN auth.identities i ON u.id = i.user_id
|
||||
LEFT JOIN public.profile p ON u.id = p.id -- LEFT JOIN with public.profile
|
||||
GROUP BY u.id, p.fullname
|
||||
`
|
||||
)
|
||||
.then((res) => res.rows as User[]);
|
||||
// type User = SupabaseUser & {
|
||||
// is_super_admin: boolean;
|
||||
// raw_user_meta_data: {
|
||||
// avatar_url: string;
|
||||
// };
|
||||
// encrypted_password: string;
|
||||
// email_confirmed_at: string;
|
||||
// created_at: string;
|
||||
// updated_at: string;
|
||||
// is_anonymous: boolean;
|
||||
// profile_fullname: string;
|
||||
// identities: {
|
||||
// provider: string;
|
||||
// identity_data: {
|
||||
// sub: string;
|
||||
// email: string;
|
||||
// };
|
||||
// created_at: string;
|
||||
// updated_at: string;
|
||||
// };
|
||||
// };
|
||||
|
||||
console.log('users length', users.length);
|
||||
// const migrateFromSupabase = async () => {
|
||||
// const ctx = await auth.$context;
|
||||
// const db = ctx.options.database as Pool;
|
||||
// const users = await db
|
||||
// .query(
|
||||
// `
|
||||
// SELECT
|
||||
// u.*,
|
||||
// COALESCE(
|
||||
// json_agg(
|
||||
// i.* ORDER BY i.id
|
||||
// ) FILTER (WHERE i.id IS NOT NULL),
|
||||
// '[]'::json
|
||||
// ) as identities,
|
||||
// p.fullname as profile_fullname -- Select fullname from public.profile
|
||||
// FROM auth.users u
|
||||
// LEFT JOIN auth.identities i ON u.id = i.user_id
|
||||
// LEFT JOIN public.profile p ON u.id = p.id -- LEFT JOIN with public.profile
|
||||
// GROUP BY u.id, p.fullname
|
||||
// `
|
||||
// )
|
||||
// .then((res) => res.rows as User[]);
|
||||
|
||||
for (const user of users) {
|
||||
if (!user.email) {
|
||||
continue;
|
||||
}
|
||||
const userName = user.profile_fullname || user.email;
|
||||
console.log('inserting user', user.email);
|
||||
// console.log('users length', users.length);
|
||||
|
||||
let shouldContinue = false;
|
||||
await ctx.adapter
|
||||
.create({
|
||||
model: 'user',
|
||||
forceAllowId: true,
|
||||
data: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: userName,
|
||||
role: user.is_super_admin ? 'admin' : user.role,
|
||||
emailVerified: !!user.email_confirmed_at,
|
||||
image: user.raw_user_meta_data?.avatar_url || '',
|
||||
createdAt: new Date(user.created_at),
|
||||
updatedAt: new Date(user.updated_at)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log('catch');
|
||||
shouldContinue = true;
|
||||
});
|
||||
// for (const user of users) {
|
||||
// if (!user.email) {
|
||||
// continue;
|
||||
// }
|
||||
// const userName = user.profile_fullname || user.email;
|
||||
// console.log('inserting user', user.email);
|
||||
|
||||
if (shouldContinue) {
|
||||
console.log('Continue');
|
||||
continue;
|
||||
}
|
||||
// let shouldContinue = false;
|
||||
// await ctx.adapter
|
||||
// .create({
|
||||
// model: 'user',
|
||||
// forceAllowId: true,
|
||||
// data: {
|
||||
// id: user.id,
|
||||
// email: user.email,
|
||||
// name: userName,
|
||||
// role: user.is_super_admin ? 'admin' : user.role,
|
||||
// emailVerified: !!user.email_confirmed_at,
|
||||
// image: user.raw_user_meta_data?.avatar_url || '',
|
||||
// createdAt: new Date(user.created_at),
|
||||
// updatedAt: new Date(user.updated_at)
|
||||
// }
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.log('catch');
|
||||
// shouldContinue = true;
|
||||
// });
|
||||
|
||||
for (const identity of user.identities) {
|
||||
const existingAccounts = await ctx.internalAdapter.findAccounts(user.id);
|
||||
// if (shouldContinue) {
|
||||
// console.log('Continue');
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (identity.provider === 'email') {
|
||||
console.log('email provider', identity);
|
||||
const hasCredential = existingAccounts.find((account) => account.providerId === 'credential');
|
||||
console.log('hasCredential', hasCredential);
|
||||
if (!hasCredential) {
|
||||
console.log('doesnt have credentials');
|
||||
await ctx.adapter
|
||||
.create({
|
||||
model: 'account',
|
||||
data: {
|
||||
userId: user.id,
|
||||
providerId: 'credential',
|
||||
accountId: user.id,
|
||||
password: user.encrypted_password,
|
||||
createdAt: new Date(user.created_at),
|
||||
updatedAt: new Date(user.updated_at)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log('catch error', e);
|
||||
});
|
||||
}
|
||||
}
|
||||
const supportedProviders = Object.keys(ctx.options.socialProviders || {});
|
||||
if (supportedProviders.includes(identity.provider)) {
|
||||
const hasAccount = existingAccounts.find((account) => account.providerId === identity.provider);
|
||||
if (!hasAccount) {
|
||||
await ctx.adapter.create({
|
||||
model: 'account',
|
||||
data: {
|
||||
userId: user.id,
|
||||
providerId: identity.provider,
|
||||
accountId: identity.identity_data?.sub,
|
||||
createdAt: new Date(identity.created_at ?? user.created_at),
|
||||
updatedAt: new Date(identity.updated_at ?? user.updated_at)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// for (const identity of user.identities) {
|
||||
// const existingAccounts = await ctx.internalAdapter.findAccounts(user.id);
|
||||
|
||||
migrateFromSupabase();
|
||||
// if (identity.provider === 'email') {
|
||||
// console.log('email provider', identity);
|
||||
// const hasCredential = existingAccounts.find((account) => account.providerId === 'credential');
|
||||
// console.log('hasCredential', hasCredential);
|
||||
// if (!hasCredential) {
|
||||
// console.log('doesnt have credentials');
|
||||
// await ctx.adapter
|
||||
// .create({
|
||||
// model: 'account',
|
||||
// data: {
|
||||
// userId: user.id,
|
||||
// providerId: 'credential',
|
||||
// accountId: user.id,
|
||||
// password: user.encrypted_password,
|
||||
// createdAt: new Date(user.created_at),
|
||||
// updatedAt: new Date(user.updated_at)
|
||||
// }
|
||||
// })
|
||||
// .catch((e) => {
|
||||
// console.log('catch error', e);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// const supportedProviders = Object.keys(ctx.options.socialProviders || {});
|
||||
// if (supportedProviders.includes(identity.provider)) {
|
||||
// const hasAccount = existingAccounts.find((account) => account.providerId === identity.provider);
|
||||
// if (!hasAccount) {
|
||||
// await ctx.adapter.create({
|
||||
// model: 'account',
|
||||
// data: {
|
||||
// userId: user.id,
|
||||
// providerId: identity.provider,
|
||||
// accountId: identity.identity_data?.sub,
|
||||
// createdAt: new Date(identity.created_at ?? user.created_at),
|
||||
// updatedAt: new Date(identity.updated_at ?? user.updated_at)
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// migrateFromSupabase();
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"zod": "^4.1.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/nodemailer": "^6.4.21",
|
||||
"prettier": "^3.2.5",
|
||||
"tsc-alias": "^1.8.16",
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as ModeSwitcher } from './dark-mode.svelte';
|
||||
|
||||
export { ModeWatcher } from 'mode-watcher';
|
||||
export * from 'mode-watcher';
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import * as z from 'zod';
|
||||
|
||||
export const ForgotPasswordSchema = z.object({
|
||||
email: z.email(),
|
||||
});
|
||||
|
||||
export type TForgotPassword = z.infer<typeof ForgotPasswordSchema>;
|
||||
@@ -0,0 +1,9 @@
|
||||
import * as z from 'zod';
|
||||
|
||||
export const ResetPasswordSchema = z.object({
|
||||
password: z.string().min(8),
|
||||
confirmPassword: z.string().min(8),
|
||||
token: z.string(),
|
||||
});
|
||||
|
||||
export type TResetPassword = z.infer<typeof ResetPasswordSchema>;
|
||||
Generated
+205
-164
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user