fix: get branch full working

This commit is contained in:
Rotimi Best
2025-11-25 17:18:15 +00:00
parent 8533e83f03
commit e0e3c8a01e
10 changed files with 396 additions and 327 deletions
-2
View File
@@ -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",
+7
View File
@@ -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;
+2
View File
@@ -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",
+163 -159
View File
@@ -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();
+1
View File
@@ -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 -1
View File
@@ -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>;
+205 -164
View File
File diff suppressed because it is too large Load Diff