Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
!/AGENTS.md
!/CONTEXT.md
!/LICENSE
!/SECURITY.md
!/CONTRIBUTING.md
!/CODE_OF_CONDUCT.md
!/CHANGELOG.md
!/README.md
!/README.zh-CN.md
!/package.json
Expand Down
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Changelog

## Unreleased

This is release-candidate preparation, not an official stable release or a
production-readiness declaration.

- Normalize Management problem difficulty at the API boundary, with regression tests.
- Restore database-generated problem IDs with an additive owner migration;
preserve existing IDs and validated test-case references. Carry complete
create/update fields across the owner seam and return nested detail readback.
Persist detail before the first version snapshot and roll back rejected child
writes. Extend real-service acceptance through persisted create/edit and Console visibility.
- Restore Management's language-menu CI contract and test actual selection-event
handling and the current-language indicator.
- Refresh Agent repository-document version binding while preserving license,
content, permission and source-integrity rejection gates.
- Allocate backend service version `1.0.4` in reactor and owner POMs; preserve
immutable image tags and require the complete nine-service release manifest.
- Document a separate disposable fresh-volume installation procedure. Keep
canonical migrations and sample data, insert-only backfill, parity, writer
isolation, backups and legacy upgrade/rollback gates intact.
- Make already-isolated Submission cutover a verified no-op rather than
restoring legacy grants after an empty REVOKE.
- Redact the transient WebSocket handshake credential and consume it at CONNECT;
authentication remains cookie-only and fail-closed.

The Agent remains experimental and disabled by default. Tests using scripted
models do not establish real-model quality, budget readiness or production
acceptance. Local Docker/browser evidence is scoped to disposable local services.

Before an official release, retain the tested source commit, successful CI and
real-service acceptance evidence, full signed nine-service digest manifest,
SBOM/provenance/scan evidence and release notes as durable assets. The source-only
bundle is not deployment input. Production deployment, release publication and
branch-protection changes require separate approval.
17 changes: 17 additions & 0 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Community expectations

Be respectful, specific and constructive in issues, reviews and discussions.
Critique the work rather than the person. Harassment, threats, discrimination,
sexualized conduct, doxxing and disclosure of another person's private information
are unacceptable. Respect maintainers' decisions about scope and moderation.

These expectations apply in project-managed spaces. Maintainers may edit or
remove harmful content, close discussions, or restrict participation. Moderation
should be proportionate to the behavior and protect the people involved.

The maintainer is [DavidHLP](https://github.com/DavidHLP). Use contact details the
maintainer publishes on that profile for private conduct concerns. If no private
contact is listed, request a private contact channel in a minimal repository issue
without naming people or publishing evidence. This document does not invent a
private email address or promise a response deadline. Security vulnerabilities
have a separate verified reporting channel in [SECURITY.md](SECURITY.md).
27 changes: 27 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Contributing

Use [issues](https://github.com/DavidHLP/UltiCode/issues) for reproducible bugs and
scoped proposals. Include the revision, expected and observed behavior, and a
minimal reproduction with synthetic data. Follow [SECURITY.md](SECURITY.md) for
vulnerabilities; never attach secrets or real user submissions to public reports.

Read [AGENTS.md](AGENTS.md), the nearest component guide, and the
[documentation map](docs/index.md) before changing code. Start from a branch,
preserve unrelated changes, and keep pull requests focused. Explain the concrete
behavior change and list checks as passed, failed, skipped or not run. Link the
issue when one exists; an issue is not required for a small repair.

Use the supported local setup and relevant checks in
[Development](docs/DEVELOPMENT.md). Browser acceptance requires disposable real
services; mocks and existing sample verdicts are not end-to-end evidence. Do not
run tests against production data or make paid model calls without permission.

Keep applied migrations immutable; add a forward migration when needed. Preserve
authentication, owner isolation, sandbox limits, backup/cutover and artifact
verification gates. Release tags are immutable: changed image digests need new
service versions. Publishing, deployment and merging are separate maintainer
actions, not consequences of a passing test or draft pull request.

Contributions are provided under the repository's [MIT license](LICENSE). Keep
third-party attribution and submit only material you have permission to share.
Be respectful and follow the [community expectations](CODE_OF_CONDUCT.md).
24 changes: 24 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Security policy

UltiCode is under active development. This repository has no published stable
GitHub Release or production security certification. Reports against the current
`main` branch are welcome; include the exact commit or immutable image digest.
The Agent is experimental and disabled by default.

## Private reporting

Use [Security → Report a vulnerability](https://github.com/DavidHLP/UltiCode/security).
GitHub private vulnerability reporting is enabled for this repository. Do not
publish exploit details, credentials, personal data or production submissions in
an issue, pull request or discussion. No dedicated security email or response-time
SLA is advertised here.

Include the affected component and revision, impact, prerequisites, and a minimal
reproduction using synthetic data in a disposable environment. Describe any
mitigation already applied. Coordinate disclosure with the maintainer before
making the report public. Do not access other users' data or test against a
third-party deployment without its operator's permission.

The project maintainer is [DavidHLP](https://github.com/DavidHLP). Operational
hardening, backup, migration and sandbox boundaries are documented in
[Operations](docs/OPERATIONS.md) and [AGENTS.md](AGENTS.md).
10 changes: 10 additions & 0 deletions apps/management/src/api/admin/__tests__/problems.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Difficulty } from '@/api/admin/problems'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import {
problemsApi,
Expand Down Expand Up @@ -106,6 +107,15 @@ describe('problemsApi', () => {
})

describe('serializeCreateInput', () => {
it.each([['EASY', 'Easy'], ['MEDIUM', 'Medium'], ['HARD', 'Hard']])(
'uses the Java DTO difficulty spelling for %s on create and update',
(input, expected) => {
expect(serializeCreateInput({ slug: 'test', title: 'Test', difficulty: input as Difficulty }).difficulty).toBe(expected)
expect(serializeUpdateInput({ difficulty: input as Difficulty }).difficulty).toBe(expected)
},
)


it('should serialize examples, constraints, hints to JSON strings', () => {
const input: ProblemCreateInput = {
slug: 'test',
Expand Down
17 changes: 15 additions & 2 deletions apps/management/src/api/admin/problems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,12 @@ export interface ProblemQueryParams {

// ========== API Transport DTOs (match backend Java DTOs exactly) ==========

export type WireDifficulty = 'Easy' | 'Medium' | 'Hard'

export interface CreateProblemDto {
slug: string
title: string
difficulty: Difficulty
difficulty: WireDifficulty
status?: ProblemStatus
isPremium?: boolean
isPublished?: boolean
Expand All @@ -162,7 +164,7 @@ export interface LanguageConfig {
export interface UpdateProblemDto {
slug?: string
title?: string
difficulty?: Difficulty
difficulty?: WireDifficulty
status?: ProblemStatus
isPremium?: boolean
hasSolution?: boolean
Expand Down Expand Up @@ -221,9 +223,19 @@ export interface ProblemUpdateInput {

// ========== Serialization Helpers ==========

function wireDifficulty(value: string): WireDifficulty {
switch (value.toUpperCase()) {
case 'EASY': return 'Easy'
case 'MEDIUM': return 'Medium'
case 'HARD': return 'Hard'
default: throw new Error('Unsupported problem difficulty')
}
}

export function serializeCreateInput(input: ProblemCreateInput): CreateProblemDto {
return {
...input,
difficulty: wireDifficulty(input.difficulty),
examples: input.examples
? JSON.stringify(input.examples.map((ex, idx) => ({ ...ex, order: ex.order ?? idx })))
: undefined,
Expand All @@ -235,6 +247,7 @@ export function serializeCreateInput(input: ProblemCreateInput): CreateProblemDt
export function serializeUpdateInput(input: ProblemUpdateInput): UpdateProblemDto {
return {
...input,
difficulty: input.difficulty === undefined ? undefined : wireDifficulty(input.difficulty),
examples: input.examples
? JSON.stringify(input.examples.map((ex, idx) => ({ ...ex, order: ex.order ?? idx })))
: undefined,
Expand Down
48 changes: 48 additions & 0 deletions apps/management/src/components/LanguageSwitcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { mount } from '@vue/test-utils'
import { defineComponent } from 'vue'
import { describe, expect, it, vi } from 'vitest'
import { Check } from '@lucide/vue'
import LanguageSwitcher from './LanguageSwitcher.vue'

const { setLocale } = vi.hoisted(() => ({ setLocale: vi.fn() }))

vi.mock('@/composables/useLocale', () => ({
useLocale: () => ({
availableLocales: [
{ code: 'en', nativeName: 'English' },
{ code: 'zh-CN', nativeName: '简体中文' },
],
setLocale,
isCurrentLocale: (code: string) => code === 'en',
}),
}))

describe('LanguageSwitcher', () => {
it('changes locale on the menu selection event and marks the current locale', async () => {
// Reka emits select for pointer and keyboard activation. Verify the consumer
// listens to that public event, without replacing it with a click handler.
const item = defineComponent({
emits: ['select'],
template: '<div><slot /></div>',
})
const container = { template: '<div><slot /></div>' }
const wrapper = mount(LanguageSwitcher, {
global: {
mocks: { $t: (key: string) => key },
stubs: {
DropdownMenuSub: container,
DropdownMenuSubTrigger: container,
DropdownMenuSubContent: container,
DropdownMenuItem: item,
},
},
})
const items = wrapper.findAllComponents(item)
expect(items.map((entry) => entry.text())).toEqual(['English', '简体中文'])
expect(items[0]!.findComponent(Check).exists()).toBe(true)
expect(items[1]!.findComponent(Check).exists()).toBe(false)
items[1]!.vm.$emit('select')
await wrapper.vm.$nextTick()
expect(setLocale).toHaveBeenCalledExactlyOnceWith('zh-CN')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('management semantic color contracts', () => {
const localeTypes = readSource('src/i18n/types.ts')

expect(switcher).toContain('{{ localeConfig.nativeName }}')
expect(switcher).toContain('@click="setLocale(localeConfig.code)"')
expect(switcher).toContain('@select="setLocale(localeConfig.code)"')
expect(switcher).toContain('isCurrentLocale(localeConfig.code)')
expect(switcher).toContain('@ulticode/design-system/ui/')
expect(switcher).not.toContain('localeConfig.flag')
Expand Down
2 changes: 1 addition & 1 deletion apps/management/src/stores/admin/system-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function defaultAllSettings(): AllSettings {
feature_notifications: true,
feature_bookmarks: true,
feature_problem_lists: true,
feature_agent: true,
feature_agent: false,
}
}

Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ flowchart LR
2. **单写者**:Submission 和 Notification 的持久化 writer 分别只在对应 Owner;App 只保留 Submission remote adapter、Notification intent 发布和 WebSocket relay。
3. **单跳同步调用**:Admin 可调用 Auth/App/Notification 的一个 Provider;Provider 不为同一命令同步调用第三个 Provider。跨边界副作用使用 Outbox/Inbox/Streams。
4. **本地事务**:强一致 invariant 留在一个 Owner 的 MySQL 事务内;Redis、SMTP、WebSocket、对象存储、搜索和审计使用 outbox、inbox、lease、fence 或补偿。
5. **安全边界**:共享 `platform/web-security` 负责 JWT/JWKS、Cookie CSRF、委托断言和 replay guard;`platform/observability` 在 OTLP authorization 配置时 fail closed,拒绝非 HTTPS endpoint;各 Owner 只保留路由与策略包装。访问令牌只来自 HttpOnly cookie;WebSocket 只接受 `access_token` cookie。
5. **安全边界**:共享 `platform/web-security` 负责 JWT/JWKS、Cookie CSRF、委托断言和 replay guard;`platform/observability` 在 OTLP authorization 配置时 fail closed,拒绝非 HTTPS endpoint;各 Owner 只保留路由与策略包装。访问令牌只来自 HttpOnly cookie;WebSocket 只接受 `access_token` cookie。Handshake 暂存凭据使用日志可安全呈现的 opaque wrapper;CONNECT 消费后移除凭据,session/debug 日志不输出原始 token。

### 查找入口

Expand Down
Loading
Loading