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