refactor[db]: store time as unix ms

Co-authored-by: Ona <no-reply@ona.com>
This commit is contained in:
2025-10-03 21:23:51 +00:00
parent 1d8a117b93
commit 0e686a1f85
11 changed files with 132 additions and 15 deletions

View File

@@ -13,6 +13,7 @@
"name": "@fileone/convex", "name": "@fileone/convex",
"dependencies": { "dependencies": {
"@fileone/path": "workspace:*", "@fileone/path": "workspace:*",
"convex-helpers": "^0.1.104",
}, },
"peerDependencies": { "peerDependencies": {
"convex": "^1.27.0", "convex": "^1.27.0",

90
packages/convex/README.md Normal file
View File

@@ -0,0 +1,90 @@
# Welcome to your Convex functions directory!
Write your Convex functions here.
See https://docs.convex.dev/functions for more.
A query function that takes two arguments looks like:
```ts
// convex/myFunctions.ts
import { query } from "./_generated/server";
import { v } from "convex/values";
export const myQueryFunction = query({
// Validators for arguments.
args: {
first: v.number(),
second: v.string(),
},
// Function implementation.
handler: async (ctx, args) => {
// Read the database as many times as you need here.
// See https://docs.convex.dev/database/reading-data.
const documents = await ctx.db.query("tablename").collect();
// Arguments passed from the client are properties of the args object.
console.log(args.first, args.second);
// Write arbitrary JavaScript here: filter, aggregate, build derived data,
// remove non-public properties, or create new objects.
return documents;
},
});
```
Using this query function in a React component looks like:
```ts
const data = useQuery(api.myFunctions.myQueryFunction, {
first: 10,
second: "hello",
});
```
A mutation function looks like:
```ts
// convex/myFunctions.ts
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const myMutationFunction = mutation({
// Validators for arguments.
args: {
first: v.string(),
second: v.string(),
},
// Function implementation.
handler: async (ctx, args) => {
// Insert or modify documents in the database here.
// Mutations can also read from the database like queries.
// See https://docs.convex.dev/database/writing-data.
const message = { body: args.first, author: args.second };
const id = await ctx.db.insert("messages", message);
// Optionally, return a value from your mutation.
return await ctx.db.get(id);
},
});
```
Using this mutation function in a React component looks like:
```ts
const mutation = useMutation(api.myFunctions.myMutationFunction);
function handleButtonPress() {
// fire and forget, the most common way to use mutations
mutation({ first: "Hello!", second: "me" });
// OR
// use the result once the mutation has completed
mutation({ first: "Hello!", second: "me" }).then((result) =>
console.log(result),
);
}
```
Use the Convex CLI to push your functions to a deployment. See everything
the Convex CLI can do by running `npx convex -h` in your project root
directory. To learn more, launch the docs with `npx convex docs`.

View File

@@ -13,6 +13,7 @@ import type {
FilterApi, FilterApi,
FunctionReference, FunctionReference,
} from "convex/server"; } from "convex/server";
import type * as admin from "../admin.js";
import type * as files from "../files.js"; import type * as files from "../files.js";
import type * as filesystem from "../filesystem.js"; import type * as filesystem from "../filesystem.js";
import type * as functions from "../functions.js"; import type * as functions from "../functions.js";
@@ -32,6 +33,7 @@ import type * as users from "../users.js";
* ``` * ```
*/ */
declare const fullApi: ApiFromModules<{ declare const fullApi: ApiFromModules<{
admin: typeof admin;
files: typeof files; files: typeof files;
filesystem: typeof filesystem; filesystem: typeof filesystem;
functions: typeof functions; functions: typeof functions;

View File

@@ -81,7 +81,7 @@ export const saveFile = authenticatedMutation({
mimeType: v.optional(v.string()), mimeType: v.optional(v.string()),
}, },
handler: async (ctx, { name, storageId, directoryId, size, mimeType }) => { handler: async (ctx, { name, storageId, directoryId, size, mimeType }) => {
const now = new Date().toISOString() const now = Date.now()
await ctx.db.insert("files", { await ctx.db.insert("files", {
name, name,

View File

@@ -70,7 +70,7 @@ export const moveToTrash = authenticatedMutation({
case FileType.File: case FileType.File:
return ctx.db return ctx.db
.patch(handle.id, { .patch(handle.id, {
deletedAt: new Date().toISOString(), deletedAt: Date.now(),
}) })
.then(() => handle) .then(() => handle)
case FileType.Directory: case FileType.Directory:

View File

@@ -144,7 +144,7 @@ export async function create(
) )
} }
const now = new Date().toISOString() const now = Date.now()
return await ctx.db.insert("directories", { return await ctx.db.insert("directories", {
name, name,
parentId, parentId,
@@ -216,7 +216,7 @@ export async function move(
ignoredHandles.add(handle) ignoredHandles.add(handle)
} else { } else {
promises.push( promises.push(
ctx.db.patch(handle.id, { parentId: targetDirectory.id }), ctx.db.patch(handle.id, { parentId: targetDirectory.id, updatedAt: Date.now() }),
) )
} }
} }
@@ -239,7 +239,7 @@ export async function moveToTrashRecursive(
ctx: AuthenticatedMutationCtx, ctx: AuthenticatedMutationCtx,
handle: DirectoryHandle, handle: DirectoryHandle,
): Promise<void> { ): Promise<void> {
const now = new Date().toISOString() const now = Date.now()
const filesToDelete: Id<"files">[] = [] const filesToDelete: Id<"files">[] = []
const directoriesToDelete: Id<"directories">[] = [] const directoriesToDelete: Id<"directories">[] = []

View File

@@ -33,7 +33,7 @@ export async function renameFile(
) )
} }
await ctx.db.patch(itemId, { name: newName }) await ctx.db.patch(itemId, { name: newName, updatedAt: Date.now() })
} }
export async function move( export async function move(
@@ -90,7 +90,7 @@ export async function move(
const results = await Promise.allSettled( const results = await Promise.allSettled(
okFiles.map((handle) => okFiles.map((handle) =>
ctx.db.patch(handle.id, { directoryId: targetDirectoryHandle.id }), ctx.db.patch(handle.id, { directoryId: targetDirectoryHandle.id, updatedAt: Date.now() }),
), ),
) )

View File

@@ -39,7 +39,7 @@ export async function userOrThrow(ctx: QueryCtx | MutationCtx) {
} }
export async function register(ctx: AuthenticatedMutationCtx) { export async function register(ctx: AuthenticatedMutationCtx) {
const now = new Date().toISOString() const now = Date.now()
await Promise.all([ await Promise.all([
ctx.db.insert("users", { ctx.db.insert("users", {
jwtSubject: ctx.identity.subject, jwtSubject: ctx.identity.subject,

View File

@@ -12,9 +12,9 @@ const schema = defineSchema({
name: v.string(), name: v.string(),
size: v.number(), size: v.number(),
mimeType: v.optional(v.string()), mimeType: v.optional(v.string()),
createdAt: v.string(), createdAt: v.number(),
updatedAt: v.string(), updatedAt: v.number(),
deletedAt: v.optional(v.string()), deletedAt: v.optional(v.number()),
}) })
.index("byDirectoryId", ["userId", "directoryId", "deletedAt"]) .index("byDirectoryId", ["userId", "directoryId", "deletedAt"])
.index("byUserId", ["userId", "deletedAt"]) .index("byUserId", ["userId", "deletedAt"])
@@ -29,9 +29,9 @@ const schema = defineSchema({
name: v.string(), name: v.string(),
userId: v.id("users"), userId: v.id("users"),
parentId: v.optional(v.id("directories")), parentId: v.optional(v.id("directories")),
createdAt: v.string(), createdAt: v.number(),
updatedAt: v.string(), updatedAt: v.number(),
deletedAt: v.optional(v.string()), deletedAt: v.optional(v.number()),
}) })
.index("byUserId", ["userId", "deletedAt"]) .index("byUserId", ["userId", "deletedAt"])
.index("byParentId", ["userId", "parentId", "deletedAt"]) .index("byParentId", ["userId", "parentId", "deletedAt"])

View File

@@ -0,0 +1,25 @@
{
/* This TypeScript project config describes the environment that
* Convex functions run in and is used to typecheck them.
* You can modify it, but some settings are required to use Convex.
*/
"compilerOptions": {
/* These settings are not required by Convex and can be modified. */
"allowJs": true,
"strict": true,
"moduleResolution": "Bundler",
"jsx": "react-jsx",
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
/* These compiler options are required by Convex */
"target": "ESNext",
"lib": ["ES2021", "dom"],
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"isolatedModules": true,
"noEmit": true
},
"include": ["./**/*"],
"exclude": ["./_generated"]
}

View File

@@ -19,7 +19,6 @@ function RouteComponent() {
useEffect(() => { useEffect(() => {
if (!isLoadingConvexAuth && isAuthenticated && !isSyncingUser) { if (!isLoadingConvexAuth && isAuthenticated && !isSyncingUser) {
console.log({ isLoadingConvexAuth, isAuthenticated, isSyncingUser })
syncUser(undefined, { syncUser(undefined, {
onSuccess: () => { onSuccess: () => {
navigate({ navigate({