Initial commit for project Spoon!
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
import { ConvexError, v } from 'convex/values';
|
||||
|
||||
import type { Doc } from './_generated/dataModel';
|
||||
import { mutation, query } from './_generated/server';
|
||||
import {
|
||||
getOwnedSpoon,
|
||||
getRequiredUserId,
|
||||
optionalText,
|
||||
requireText,
|
||||
} from './model';
|
||||
|
||||
const provider = v.union(
|
||||
v.literal('github'),
|
||||
v.literal('gitea'),
|
||||
v.literal('gitlab'),
|
||||
v.literal('other'),
|
||||
);
|
||||
|
||||
const visibility = v.union(
|
||||
v.literal('public'),
|
||||
v.literal('private'),
|
||||
v.literal('internal'),
|
||||
v.literal('unknown'),
|
||||
);
|
||||
|
||||
const maintenanceMode = v.union(
|
||||
v.literal('watch'),
|
||||
v.literal('auto_pr'),
|
||||
v.literal('paused'),
|
||||
);
|
||||
|
||||
const syncCadence = v.union(
|
||||
v.literal('daily'),
|
||||
v.literal('weekly'),
|
||||
v.literal('manual'),
|
||||
);
|
||||
|
||||
const productionRefStrategy = v.union(
|
||||
v.literal('default_branch'),
|
||||
v.literal('latest_release'),
|
||||
v.literal('tag_pattern'),
|
||||
);
|
||||
|
||||
const spoonStatus = v.union(
|
||||
v.literal('draft'),
|
||||
v.literal('active'),
|
||||
v.literal('needs_connection'),
|
||||
v.literal('paused'),
|
||||
v.literal('archived'),
|
||||
);
|
||||
|
||||
const hasForkMetadata = (args: {
|
||||
forkOwner?: string;
|
||||
forkRepo?: string;
|
||||
forkUrl?: string;
|
||||
}) =>
|
||||
Boolean(
|
||||
args.forkOwner?.trim() && args.forkRepo?.trim() && args.forkUrl?.trim(),
|
||||
);
|
||||
|
||||
export const listMine = query({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
const spoons = await ctx.db
|
||||
.query('spoons')
|
||||
.withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
|
||||
.order('desc')
|
||||
.collect();
|
||||
return spoons.filter((spoon) => spoon.status !== 'archived');
|
||||
},
|
||||
});
|
||||
|
||||
export const get = query({
|
||||
args: { spoonId: v.id('spoons') },
|
||||
handler: async (ctx, { spoonId }) => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
return getOwnedSpoon(ctx, spoonId, ownerId);
|
||||
},
|
||||
});
|
||||
|
||||
export const createManual = mutation({
|
||||
args: {
|
||||
name: v.string(),
|
||||
description: v.optional(v.string()),
|
||||
provider,
|
||||
upstreamOwner: v.string(),
|
||||
upstreamRepo: v.string(),
|
||||
upstreamDefaultBranch: v.string(),
|
||||
upstreamUrl: v.string(),
|
||||
forkOwner: v.optional(v.string()),
|
||||
forkRepo: v.optional(v.string()),
|
||||
forkDefaultBranch: v.optional(v.string()),
|
||||
forkUrl: v.optional(v.string()),
|
||||
visibility,
|
||||
maintenanceMode,
|
||||
syncCadence,
|
||||
productionRefStrategy,
|
||||
tagPattern: v.optional(v.string()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
const now = Date.now();
|
||||
const forkOwner = optionalText(args.forkOwner);
|
||||
const forkRepo = optionalText(args.forkRepo);
|
||||
const forkUrl = optionalText(args.forkUrl);
|
||||
const status = hasForkMetadata({ forkOwner, forkRepo, forkUrl })
|
||||
? 'draft'
|
||||
: 'needs_connection';
|
||||
|
||||
return await ctx.db.insert('spoons', {
|
||||
ownerId,
|
||||
name: requireText(args.name, 'Spoon name'),
|
||||
description: optionalText(args.description),
|
||||
provider: args.provider,
|
||||
upstreamOwner: requireText(args.upstreamOwner, 'Upstream owner'),
|
||||
upstreamRepo: requireText(args.upstreamRepo, 'Upstream repository'),
|
||||
upstreamDefaultBranch: requireText(
|
||||
args.upstreamDefaultBranch,
|
||||
'Upstream default branch',
|
||||
),
|
||||
upstreamUrl: requireText(args.upstreamUrl, 'Upstream URL'),
|
||||
forkOwner,
|
||||
forkRepo,
|
||||
forkDefaultBranch: optionalText(args.forkDefaultBranch),
|
||||
forkUrl,
|
||||
visibility: args.visibility,
|
||||
maintenanceMode: args.maintenanceMode,
|
||||
syncCadence: args.syncCadence,
|
||||
productionRefStrategy: args.productionRefStrategy,
|
||||
tagPattern: optionalText(args.tagPattern),
|
||||
status,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const updateSettings = mutation({
|
||||
args: {
|
||||
spoonId: v.id('spoons'),
|
||||
maintenanceMode: v.optional(maintenanceMode),
|
||||
syncCadence: v.optional(syncCadence),
|
||||
productionRefStrategy: v.optional(productionRefStrategy),
|
||||
tagPattern: v.optional(v.string()),
|
||||
status: v.optional(spoonStatus),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
await getOwnedSpoon(ctx, args.spoonId, ownerId);
|
||||
const patch: Partial<Doc<'spoons'>> = { updatedAt: Date.now() };
|
||||
if (args.maintenanceMode) patch.maintenanceMode = args.maintenanceMode;
|
||||
if (args.syncCadence) patch.syncCadence = args.syncCadence;
|
||||
if (args.productionRefStrategy) {
|
||||
patch.productionRefStrategy = args.productionRefStrategy;
|
||||
}
|
||||
if (args.tagPattern !== undefined)
|
||||
patch.tagPattern = optionalText(args.tagPattern);
|
||||
if (args.status) patch.status = args.status;
|
||||
await ctx.db.patch(args.spoonId, patch);
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
|
||||
export const archive = mutation({
|
||||
args: { spoonId: v.id('spoons') },
|
||||
handler: async (ctx, { spoonId }) => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
const spoon = await getOwnedSpoon(ctx, spoonId, ownerId);
|
||||
if (spoon.status === 'archived')
|
||||
throw new ConvexError('Spoon is archived.');
|
||||
await ctx.db.patch(spoonId, {
|
||||
status: 'archived',
|
||||
updatedAt: Date.now(),
|
||||
});
|
||||
return { success: true };
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user