Made great progress on monorepo & auth for next. Very happy with work!

This commit is contained in:
gib committed 2026-01-12 11:55:15 -06:00
1 parent 72f11f0b02
commit 321fecb5e1
58 files changed
+1266 -222

No files matched your search

+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15"],{"key":"16","value":"17"},{"key":"18","value":"19"},{"key":"20","value":"21"},{"key":"22","value":"23"},{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.cache/.prettiercache",{"size":3699,"mtime":1766222924000,"hash":"46"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/dataModel.d.ts",{"size":1726,"mtime":1768155639236,"hash":"47","data":"48"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.gitignore",{"size":16,"mtime":1766222924000,"hash":"49"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/utils.ts",{"size":635,"mtime":1768155639993,"hash":"50","data":"51"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/tsconfig.json",{"size":732,"mtime":1766222924000,"hash":"52","data":"53"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/package.json",{"size":1120,"mtime":1766223702000,"hash":"54","data":"55"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.js",{"size":3453,"mtime":1768155639358,"hash":"56","data":"57"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.config.js",{"size":128,"mtime":1768155639410,"hash":"58","data":"59"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/notes.ts",{"size":1632,"mtime":1768155639618,"hash":"60","data":"61"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.d.ts",{"size":5540,"mtime":1768155639312,"hash":"62","data":"63"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.d.ts",{"size":845,"mtime":1768155639122,"hash":"64","data":"65"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.js",{"size":414,"mtime":1768155639185,"hash":"66","data":"67"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/README.md",{"size":2525,"mtime":1768155639908,"hash":"68","data":"69"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/openai.ts",{"size":2036,"mtime":1768155639718,"hash":"70","data":"71"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/schema.ts",{"size":267,"mtime":1768155639933,"hash":"72","data":"73"},"bf09c6d61dd6a010bca24cb05271176d","ce3275ccdd28673d7e9280b33792057c",{"hashOfOptions":"74"},"175b1a771387d8b7e26145c3d04e0475","6a3198db454396c63e99ee08065726bd",{"hashOfOptions":"75"},"cfa98923457caed911ec68b626ef4234",{"hashOfOptions":"76"},"355d413e63d71e7e2107534f0d82fa31",{"hashOfOptions":"77"},"a8efc7f01f2b2dcb19115187a6f5f935",{"hashOfOptions":"78"},"449225bb5e883c1319e2ade335cdf0ad",{"hashOfOptions":"79"},"4b3c533672165e2af8d66b7149f85e1b",{"hashOfOptions":"80"},"896efcfb7466854312acff830348fec5",{"hashOfOptions":"81"},"8effb22a4ee737ecaeb923db430289f9",{"hashOfOptions":"82"},"0f5f819cfc410629d8ba1209956a037c",{"hashOfOptions":"83"},"f5aa8269478f140adb4ccf0470dd1244",{"hashOfOptions":"84"},"e06978cd8943c0adfa4d2a855c8f3627",{"hashOfOptions":"85"},"0d3d6e28d2429a34e666323218320d74",{"hashOfOptions":"86"},"1690245769","3139400620","2702670119","1723032970","1018051749","1593187711","1215337372","3786452325","2623319630","3362284046","3941219177","30277581","3515117182"]
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23"],{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},{"key":"46","value":"47"},{"key":"48","value":"49"},{"key":"50","value":"51"},{"key":"52","value":"53"},{"key":"54","value":"55"},{"key":"56","value":"57"},{"key":"58","value":"59"},{"key":"60","value":"61"},{"key":"62","value":"63"},{"key":"64","value":"65"},{"key":"66","value":"67"},{"key":"68","value":"69"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.cache/.prettiercache",{"size":3533,"mtime":1768171236843,"hash":"70"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/crons.ts",{"size":528,"mtime":1768239263662,"hash":"71","data":"72"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.ts",{"size":3630,"mtime":1768239263654,"hash":"73","data":"74"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/password.ts",{"size":900,"mtime":1768239263701,"hash":"75","data":"76"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/dataModel.d.ts",{"size":1726,"mtime":1768239263554,"hash":"77","data":"78"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.gitignore",{"size":16,"mtime":1766222924000,"hash":"79"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/utils.ts",{"size":635,"mtime":1768155639000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.config.ts",{"size":127,"mtime":1768156477000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/tsconfig.json",{"size":732,"mtime":1766222924000,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/package.json",{"size":1300,"mtime":1768239263823,"hash":"86","data":"87"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/usesend.ts",{"size":3115,"mtime":1768164053000,"hash":"88","data":"89"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.js",{"size":3474,"mtime":1768239263584,"hash":"90","data":"91"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.d.ts",{"size":5695,"mtime":1768239263572,"hash":"92","data":"93"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/auth.ts",{"size":174,"mtime":1768188156942,"hash":"94","data":"95"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/files.ts",{"size":582,"mtime":1768239263763,"hash":"96","data":"97"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/scripts/generateKeys.mjs",{"size":520,"mtime":1768239263846,"hash":"98","data":"99"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.d.ts",{"size":1521,"mtime":1768239263527,"hash":"100","data":"101"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/index.ts",{"size":69,"mtime":1768239263861,"hash":"102","data":"103"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.js",{"size":480,"mtime":1768239263542,"hash":"104","data":"105"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/README.md",{"size":2525,"mtime":1768155639000,"hash":"106","data":"107"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/http.ts",{"size":153,"mtime":1768157491000,"hash":"108","data":"109"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/schema.ts",{"size":1197,"mtime":1768239263796,"hash":"110","data":"111"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/index.ts",{"size":142,"mtime":1768157708000,"hash":"112","data":"113"},"e6b51ffddcfeaaeaaf7d1a0453024eff","0d76c3867783aec5435b0c716f1bfc65",{"hashOfOptions":"114"},"bd827d1529ceec8f4804864db994747b",{"hashOfOptions":"115"},"55bcf7d546da727edf2c1b7e52db6c7f",{"hashOfOptions":"116"},"ce3275ccdd28673d7e9280b33792057c",{"hashOfOptions":"117"},"175b1a771387d8b7e26145c3d04e0475","6a3198db454396c63e99ee08065726bd",{"hashOfOptions":"118"},"2d4f4ccfc784af9d668d8c64726c2f6b",{"hashOfOptions":"119"},"cfa98923457caed911ec68b626ef4234",{"hashOfOptions":"120"},"5d96f2526115241c4a37250b0f45ac4d",{"hashOfOptions":"121"},"14f5e2d18e21f65282ac744b382f2d5f",{"hashOfOptions":"122"},"f42d53bd1ba24bfee1407d2098f3b9ff",{"hashOfOptions":"123"},"6719b019702fc173de16b1bdc8e8ed47",{"hashOfOptions":"124"},"f0f9ef72dc6fef350f3521d3fd8adfc7",{"hashOfOptions":"125"},"5ff3856f1a61b946df899f7c9880b72a",{"hashOfOptions":"126"},"ccf5b9105ad4ff66fce0a7133fa3db4b",{"hashOfOptions":"127"},"7c522302bfb45e9cf5f1c271b48fa247",{"hashOfOptions":"128"},"57577de5aadfa618e91739aea3630342",{"hashOfOptions":"129"},"9d0d3b8d30fb1a1207df0584bf0f4c2e",{Line truncated
+4
View File
@@ -9,9 +9,11 @@
*/
import type * as auth from "../auth.js";
import type * as crons from "../crons.js";
import type * as custom_auth_index from "../custom/auth/index.js";
import type * as custom_auth_providers_password from "../custom/auth/providers/password.js";
import type * as custom_auth_providers_usesend from "../custom/auth/providers/usesend.js";
import type * as files from "../files.js";
import type * as http from "../http.js";
import type * as utils from "../utils.js";
@@ -31,9 +33,11 @@ import type {
*/
declare const fullApi: ApiFromModules<{
auth: typeof auth;
crons: typeof crons;
"custom/auth/index": typeof custom_auth_index;
"custom/auth/providers/password": typeof custom_auth_providers_password;
"custom/auth/providers/usesend": typeof custom_auth_providers_usesend;
files: typeof files;
http: typeof http;
utils: typeof utils;
}>;
+3 -7
View File
@@ -14,10 +14,7 @@ import { action, mutation, query } from './_generated/server';
import { Password, validatePassword } from './custom/auth';
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
providers: [
Authentik({ allowDangerousEmailAccountLinking: true }),
Password,
],
providers: [Authentik({ allowDangerousEmailAccountLinking: true }), Password],
});
const getUserById = async (
@@ -39,9 +36,8 @@ const isSignedIn = async (ctx: QueryCtx): Promise<Doc<'users'> | null> => {
export const getUser = query({
args: { userId: v.optional(v.id('users')) },
handler: async (ctx, args) => {
const user = await isSignedIn(ctx);
const userId = args.userId ?? user?._id;
if (!userId) throw new ConvexError('Not authenticated or no ID provided.');
const userId = args.userId ?? (await getAuthUserId(ctx));
if (!userId) return null;
return getUserById(ctx, userId);
},
});
+23
View File
@@ -0,0 +1,23 @@
import { cronJobs } from 'convex/server';
import { api } from './_generated/api';
// Cron order: Minute Hour DayOfMonth Month DayOfWeek
const crons = cronJobs();
/* Example cron jobs
crons.cron(
// Run at 7:00 AM CST / 8:00 AM CDT
// Only on weekdays
'Schedule Automatic Lunches',
'0 13 * * 1-5',
api.statuses.automaticLunch,
);
crons.cron(
// Run at 4:00 PM CST / 5:00 PM CDT
// Only on weekdays
'End of shift (weekdays 5pm CT)',
'0 22 * * 1-5',
api.statuses.endOfShiftUpdate,
);
*/
export default crons;
@@ -1,5 +1,6 @@
import { Password as DefaultPassword } from '@convex-dev/auth/providers/Password';
import { ConvexError } from 'convex/values';
import { UseSendOTP, UseSendOTPPasswordReset } from '..';
import { DataModel } from '../../../_generated/dataModel';
+18
View File
@@ -0,0 +1,18 @@
import { getAuthUserId } from '@convex-dev/auth/server';
import { ConvexError, v } from 'convex/values';
import { mutation, query } from './_generated/server';
export const generateUploadUrl = mutation(async (ctx) => {
const userId = await getAuthUserId(ctx);
if (!userId) throw new ConvexError('Not authenticated.');
return await ctx.storage.generateUploadUrl();
});
export const getImageUrl = query({
args: { storageId: v.id('_storage') },
handler: async (ctx, { storageId }) => {
const url = await ctx.storage.getUrl(storageId);
return url ?? null;
},
});
+4 -5
View File
@@ -1,6 +1,6 @@
import { authTables } from '@convex-dev/auth/server';
import { defineSchema, defineTable } from 'convex/server';
import { v } from 'convex/values';
import { authTables } from '@convex-dev/auth/server';
const applicationTables = {
// Users contains name image & email.
@@ -10,8 +10,7 @@ const applicationTables = {
profiles: defineTable({
userId: v.id('users'),
theme_preference: v.optional(v.string()),
})
.index('userId', ['userId'])
}).index('userId', ['userId']),
};
export default defineSchema({
@@ -29,8 +28,8 @@ export default defineSchema({
phoneVerificationTime: v.optional(v.number()),
isAnonymous: v.optional(v.boolean()),
})
.index("email", ["email"])
.index('email', ['email'])
.index('name', ['name'])
.index("phone", ["phone"]),
.index('phone', ['phone']),
...applicationTables,
});
+3 -1
View File
@@ -7,7 +7,9 @@
"author": "Gib",
"license": "MIT",
"exports": {
"./types" : "./types/index.ts"
"./convex": "./convex/",
"./convex/*": "./convex/*",
"./types": "./types/index.ts"
},
"scripts": {
"dev": "bun with-env convex dev",
+6 -7
View File
@@ -1,17 +1,16 @@
#!/usr/bin/env node
import { exportJWK, exportPKCS8, generateKeyPair } from 'jose';
import { exportJWK, exportPKCS8, generateKeyPair } from "jose";
const keys = await generateKeyPair("RS256", {
const keys = await generateKeyPair('RS256', {
extractable: true,
});
const privateKey = await exportPKCS8(keys.privateKey);
const publicKey = await exportJWK(keys.publicKey);
const jwks = JSON.stringify({ keys: [{ use: "sig", ...publicKey }] });
const jwks = JSON.stringify({ keys: [{ use: 'sig', ...publicKey }] });
process.stdout.write(
`JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, " ")}"`,
`JWT_PRIVATE_KEY="${privateKey.trimEnd().replace(/\n/g, ' ')}"`,
);
process.stdout.write("\n");
process.stdout.write('\n');
process.stdout.write(`JWKS=${jwks}`);
process.stdout.write("\n");
process.stdout.write('\n');
+1 -5
View File
@@ -1,5 +1 @@
export {
PASSWORD_MIN,
PASSWORD_MAX,
PASSWORD_REGEX,
} from './auth';
export { PASSWORD_MIN, PASSWORD_MAX, PASSWORD_REGEX } from './auth';