chore: exclude local resource artifacts from main sync

This commit is contained in:
2026-05-15 10:16:45 +09:00
parent 442879313f
commit d38d022872
504 changed files with 17074 additions and 3642 deletions

3
etc/servers/work-server/src/routes/app-config.ts Executable file → Normal file
View File

@@ -111,13 +111,12 @@ export async function registerAppConfigRoutes(app: FastifyInstance) {
const parsed = z
.object({
chatTypes: z.array(z.unknown()).optional(),
customChatTypes: z.array(z.unknown()).optional(),
})
.parse(payload ?? {});
const appOrigin = getRequestAppOrigin(request);
const appDomain = getRequestAppDomain(request);
const targetChatTypes = parsed.customChatTypes ?? parsed.chatTypes ?? [];
const targetChatTypes = parsed.chatTypes ?? [];
const savedChatTypeConfig = await upsertChatTypesConfig(targetChatTypes, appOrigin, appDomain);
return {

0
etc/servers/work-server/src/routes/board.ts Executable file → Normal file
View File

39
etc/servers/work-server/src/routes/chat.ts Executable file → Normal file
View File

@@ -16,6 +16,7 @@ import {
deleteChatConversation,
ensureChatConversationTables,
getChatConversation,
listChatSourceChangeSnapshots,
listChatConversationDetailPage,
listChatConversations,
markChatConversationResponsesRead,
@@ -205,6 +206,21 @@ export async function registerChatRoutes(app: FastifyInstance) {
};
});
app.get('/api/chat/source-changes', async (request) => {
const query = z.object({
limit: z.coerce.number().int().min(1).max(500).optional(),
}).parse(request.query ?? {});
const viewerClientId = getClientIdHeader(request);
const clientId = canViewAllConversations(request) ? null : viewerClientId;
const items = await listChatSourceChangeSnapshots(clientId, query.limit ?? 300);
return {
ok: true,
items,
};
});
app.get('/api/chat/runtime', async () => {
return {
ok: true,
@@ -380,6 +396,7 @@ export async function registerChatRoutes(app: FastifyInstance) {
const payload = z.object({
sessionId: z.string().trim().min(1).max(120),
title: z.string().trim().max(200).optional(),
requestBadgeLabel: z.string().trim().max(120).optional().nullable(),
chatTypeId: z.string().trim().max(120).nullable().optional(),
lastChatTypeId: z.string().trim().max(120).nullable().optional(),
generalSectionName: z.string().trim().max(120).optional().nullable(),
@@ -393,6 +410,7 @@ export async function registerChatRoutes(app: FastifyInstance) {
sessionId: payload.sessionId,
clientId: clientId || null,
title: payload.title ?? '새 대화',
requestBadgeLabel: payload.requestBadgeLabel ?? null,
chatTypeId: payload.chatTypeId ?? null,
lastChatTypeId: payload.lastChatTypeId ?? payload.chatTypeId ?? null,
generalSectionName: payload.generalSectionName ?? null,
@@ -509,6 +527,7 @@ export async function registerChatRoutes(app: FastifyInstance) {
}).parse(request.params ?? {});
const payload = z.object({
title: z.string().trim().min(1).max(200).optional(),
requestBadgeLabel: z.string().trim().max(120).optional().nullable(),
chatTypeId: z.string().trim().max(120).optional().nullable(),
lastChatTypeId: z.string().trim().max(120).optional().nullable(),
generalSectionName: z.string().trim().max(120).optional().nullable(),
@@ -528,12 +547,22 @@ export async function registerChatRoutes(app: FastifyInstance) {
const item = await updateChatConversationContext(params.sessionId, {
title: payload.title ?? current.title,
requestBadgeLabel:
Object.prototype.hasOwnProperty.call(payload, 'requestBadgeLabel') ? payload.requestBadgeLabel ?? null : undefined,
clientId: current.clientId,
chatTypeId: payload.chatTypeId ?? current.chatTypeId,
lastChatTypeId: payload.lastChatTypeId ?? current.lastChatTypeId ?? current.chatTypeId,
generalSectionName: payload.generalSectionName ?? current.generalSectionName,
contextLabel: payload.contextLabel ?? current.contextLabel,
contextDescription: payload.contextDescription ?? current.contextDescription,
chatTypeId: Object.prototype.hasOwnProperty.call(payload, 'chatTypeId') ? payload.chatTypeId ?? null : undefined,
lastChatTypeId:
Object.prototype.hasOwnProperty.call(payload, 'lastChatTypeId') ? payload.lastChatTypeId ?? null : undefined,
generalSectionName:
Object.prototype.hasOwnProperty.call(payload, 'generalSectionName')
? payload.generalSectionName ?? null
: undefined,
contextLabel:
Object.prototype.hasOwnProperty.call(payload, 'contextLabel') ? payload.contextLabel ?? null : undefined,
contextDescription:
Object.prototype.hasOwnProperty.call(payload, 'contextDescription')
? payload.contextDescription ?? null
: undefined,
notifyOffline: payload.notifyOffline ?? current.notifyOffline,
});

View File

0
etc/servers/work-server/src/routes/crud.ts Executable file → Normal file
View File

0
etc/servers/work-server/src/routes/ddl.ts Executable file → Normal file
View File

0
etc/servers/work-server/src/routes/error-log.ts Executable file → Normal file
View File

0
etc/servers/work-server/src/routes/health.ts Executable file → Normal file
View File

56
etc/servers/work-server/src/routes/notification.ts Executable file → Normal file
View File

@@ -1,21 +1,16 @@
import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import {
listIosNotificationTokens,
listWebPushSubscriptions,
getAutomationNotificationPreference,
getWebPushConfig,
registerIosNotificationToken,
registerAutomationNotificationPreferenceSchema,
registerIosTokenSchema,
registerWebPushSubscription,
registerWebPushSubscriptionSchema,
sendNotifications,
sendIosNotificationSchema,
setupNotificationTables,
upsertAutomationNotificationPreference,
unregisterIosNotificationToken,
unregisterIosTokenSchema,
unregisterWebPushSubscription,
unregisterWebPushSubscriptionSchema,
} from '../services/notification-service.js';
@@ -31,14 +26,10 @@ import {
} from '../services/notification-message-service.js';
const automationNotificationPreferenceQuerySchema = z.object({
targetKind: z.enum(['client', 'ios-token', 'ios-token-client', 'web-endpoint']).optional(),
targetKind: z.enum(['client', 'web-endpoint']).optional(),
targetId: z.string().trim().min(1).max(1000).optional(),
});
type AutomationNotificationPreferenceTargetKind = NonNullable<
z.infer<typeof automationNotificationPreferenceQuerySchema>['targetKind']
>;
function getClientIdHeader(request: { headers: Record<string, string | string[] | undefined> }) {
const rawClientId = request.headers['x-client-id'];
const clientId = Array.isArray(rawClientId) ? rawClientId[0] : rawClientId;
@@ -48,10 +39,6 @@ function getClientIdHeader(request: { headers: Record<string, string | string[]
export async function registerNotificationRoutes(app: FastifyInstance) {
app.post('/api/notifications/setup', async () => setupNotificationTables());
app.get('/api/notifications/tokens', async () => ({
items: await listIosNotificationTokens(),
}));
app.get('/api/notifications/subscriptions/web', async () => ({
items: await listWebPushSubscriptions(),
}));
@@ -60,9 +47,10 @@ export async function registerNotificationRoutes(app: FastifyInstance) {
app.get('/api/notifications/messages', async (request) => {
const query = notificationMessageListQuerySchema.parse(request.query ?? {});
const clientId = getClientIdHeader(request);
return {
ok: true,
...(await listNotificationMessages(query)),
...(await listNotificationMessages(query, clientId)),
};
});
@@ -130,7 +118,7 @@ export async function registerNotificationRoutes(app: FastifyInstance) {
return {
ok: true,
automation: await getAutomationNotificationPreferenceWithFallback(targetId, targetKind),
automation: await getAutomationNotificationPreference(targetId, targetKind),
};
});
@@ -154,16 +142,6 @@ export async function registerNotificationRoutes(app: FastifyInstance) {
}
});
app.put('/api/notifications/tokens/ios', async (request) => {
const payload = registerIosTokenSchema.parse(request.body ?? {});
return registerIosNotificationToken(payload);
});
app.delete('/api/notifications/tokens/ios', async (request) => {
const payload = unregisterIosTokenSchema.parse(request.body ?? {});
return unregisterIosNotificationToken(payload.token);
});
app.put('/api/notifications/subscriptions/web', async (request) => {
const payload = registerWebPushSubscriptionSchema.parse(request.body ?? {});
return registerWebPushSubscription(payload);
@@ -184,29 +162,3 @@ export async function registerNotificationRoutes(app: FastifyInstance) {
return sendNotifications(payload);
});
}
async function getAutomationNotificationPreferenceWithFallback(
targetId: string,
targetKind: AutomationNotificationPreferenceTargetKind,
) {
const automation = await getAutomationNotificationPreference(targetId, targetKind);
if (automation || targetKind !== 'ios-token-client') {
return automation;
}
const [token, clientId] = targetId.split('::client::');
if (token?.trim()) {
const tokenAutomation = await getAutomationNotificationPreference(token.trim(), 'ios-token');
if (tokenAutomation) {
return tokenAutomation;
}
}
if (clientId?.trim()) {
return getAutomationNotificationPreference(clientId.trim(), 'client');
}
return null;
}

0
etc/servers/work-server/src/routes/plan.ts Executable file → Normal file
View File

0
etc/servers/work-server/src/routes/schema.ts Executable file → Normal file
View File

40
etc/servers/work-server/src/routes/server-command.ts Executable file → Normal file
View File

@@ -19,6 +19,36 @@ const restartReservationBodySchema = z.object({
autoExecuteDelaySeconds: z.number().int().min(1).max(300).optional(),
});
function getImmediateRestartBlockInfo(
key: z.infer<typeof serverCommandParamSchema>['key'],
workloadSummary: Awaited<ReturnType<typeof getRestartReservationWorkloadSummary>>,
) {
const codexPendingCount = workloadSummary.codexRunningCount + workloadSummary.codexQueuedCount;
const automationPendingCount = workloadSummary.automationRunningCount + workloadSummary.automationQueuedCount;
if (key === 'test') {
const pendingCount = codexPendingCount + automationPendingCount;
if (pendingCount > 0) {
return {
pendingCount,
message: `진행 중인 Codex Live/자동화 작업 ${pendingCount}건이 있어 즉시 재기동할 수 없습니다. 재기동 예약을 사용해 주세요.`,
};
}
return null;
}
if (key === 'work-server' && automationPendingCount > 0) {
return {
pendingCount: automationPendingCount,
message: `진행 중인 자동화 작업 ${automationPendingCount}건이 있어 즉시 재기동할 수 없습니다. 재기동 예약을 사용해 주세요.`,
};
}
return null;
}
function getRequestAccessToken(request: FastifyRequest) {
const tokenHeader = request.headers['x-access-token'];
return Array.isArray(tokenHeader) ? tokenHeader[0]?.trim() ?? '' : String(tokenHeader ?? '').trim();
@@ -75,17 +105,13 @@ export async function registerServerCommandRoutes(app: FastifyInstance) {
if (key === 'test' || key === 'work-server') {
const workloadSummary = await getRestartReservationWorkloadSummary();
const pendingCount =
workloadSummary.codexRunningCount
+ workloadSummary.codexQueuedCount
+ workloadSummary.automationRunningCount
+ workloadSummary.automationQueuedCount;
const blockInfo = getImmediateRestartBlockInfo(key, workloadSummary);
if (pendingCount > 0) {
if (blockInfo) {
reply.status(409);
return {
ok: false,
message: `진행 중인 Codex Live/자동화 작업 ${pendingCount}건이 있어 즉시 재기동할 수 없습니다. 재기동 예약을 사용해 주세요.`,
message: blockInfo.message,
workloadSummary,
};
}

0
etc/servers/work-server/src/routes/visitor-history.ts Executable file → Normal file
View File