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", "@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",
+7
View File
@@ -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;
+2
View File
@@ -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",
+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 { 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();
+1
View File
@@ -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 -1
View File
@@ -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>;
+205 -164
View File
File diff suppressed because it is too large Load Diff