Merge pull request '[E9-3b-ui] desktop: форма publishPackage в Столе разработчика' (#80) from feat/E9-3b-ui-publish-package into feat/E9-3b-sub-subscribe

This commit was merged in pull request #80.
This commit is contained in:
2026-06-04 11:23:21 +00:00
10 changed files with 1949 additions and 18 deletions
+108
View File
@@ -99,6 +99,10 @@ export const AllTypesProps: Record<string,any> = {
statuses:"ApprovalStatus"
},
ApprovalStatus: "enum" as const,
ApproveModerationInputDTO:{
scope:"ReleaseScopeInputDTO"
},
ApproveModerationStatus: "enum" as const,
AssetContributionActGenerateDocumentInput:{
},
@@ -631,6 +635,7 @@ export const AllTypesProps: Record<string,any> = {
ModerateRequestInput:{
},
ModerationStatusEnum: "enum" as const,
MoveCapitalIssueToComponentInput:{
},
@@ -647,6 +652,9 @@ export const AllTypesProps: Record<string,any> = {
addTrustedAccount:{
data:"AddTrustedAccountInput"
},
approveModeration:{
data:"ApproveModerationInputDTO"
},
authorizeDecision:{
data:"AuthorizeDecisionInput"
},
@@ -1136,9 +1144,15 @@ export const AllTypesProps: Record<string,any> = {
prohibitRequest:{
data:"ProhibitRequestInput"
},
publishPackage:{
data:"PublishPackageInputDTO"
},
publishProjectOfFreeDecision:{
data:"PublishProjectFreeDecisionInput"
},
publishRelease:{
data:"PublishReleaseInputDTO"
},
publishRequest:{
data:"PublishRequestInput"
},
@@ -1154,6 +1168,9 @@ export const AllTypesProps: Record<string,any> = {
registerParticipant:{
data:"RegisterParticipantInput"
},
rejectModeration:{
data:"RejectModerationInputDTO"
},
resetKey:{
data:"ResetKeyInput"
},
@@ -1187,6 +1204,9 @@ export const AllTypesProps: Record<string,any> = {
startResetKey:{
data:"StartResetKeyInput"
},
subscribePackage:{
data:"SubscribePackageInputDTO"
},
supplyOnRequest:{
data:"SupplyOnRequestInput"
},
@@ -1321,9 +1341,15 @@ export const AllTypesProps: Record<string,any> = {
},
ProjectStatus: "enum" as const,
PublishPackageInputDTO:{
},
PublishProjectFreeDecisionInput:{
document:"ProjectFreeDecisionSignedDocumentInput"
},
PublishReleaseInputDTO:{
manifest:"JSON"
},
PublishRequestInput:{
},
@@ -1337,6 +1363,12 @@ export const AllTypesProps: Record<string,any> = {
agreements:{
filter:"AgreementFilter",
options:"PaginationInput"
},
appsCatalogPendingModerations:{
status:"ModerationStatusEnum"
},
appsCatalogRemotePackages:{
},
buildInitialReportEdits:{
reportType:"ReportType"
@@ -1661,6 +1693,15 @@ export const AllTypesProps: Record<string,any> = {
user_agreement:"SignedDigitalDocumentInput",
wallet_agreement:"SignedDigitalDocumentInput"
},
RejectModerationInputDTO:{
},
RejectModerationStatus: "enum" as const,
ReleaseScopeInputDTO:{
type:"ReleaseScopeType"
},
ReleaseScopeType: "enum" as const,
ReleaseTypeEnum: "enum" as const,
RemoveSecretaryRoomInput:{
},
@@ -1833,6 +1874,11 @@ export const AllTypesProps: Record<string,any> = {
SubmitVoteInput:{
votes:"VoteDistributionInput"
},
SubscribePackageInputDTO:{
},
SubscribePackageStatus: "enum" as const,
SubscriptionStateEnum: "enum" as const,
SupplyOnRequestInput:{
document:"AssetContributionActSignedDocumentInput"
},
@@ -2040,6 +2086,22 @@ export const ReturnTypes: Record<string,any> = {
status:"ApprovalStatus",
username:"String"
},
ApproveModerationResultDTO:{
error:"String",
packageId:"String",
requestId:"String",
status:"ApproveModerationStatus",
version:"String"
},
AppsCatalogRemotePackageDTO:{
compatibleSubnets:"String",
description:"String",
lastActiveVersion:"String",
packageId:"String",
publisher:"String",
rubPerMonth:"Float",
title:"String"
},
AuthSequence:{
account:"String",
sequence:"String"
@@ -3490,6 +3552,19 @@ export const ReturnTypes: Record<string,any> = {
reason:"String",
source:"RequisiteSource"
},
ModerationRequestDTO:{
brief:"String",
id:"String",
packageId:"String",
releaseType:"ReleaseTypeEnum",
requiresOverride:"Boolean",
scope:"JSON",
status:"ModerationStatusEnum",
submittedAt:"String",
submittedBy:"String",
updatedAt:"String",
version:"String"
},
MonoAccount:{
email:"String",
has_account:"Boolean",
@@ -3511,6 +3586,7 @@ export const ReturnTypes: Record<string,any> = {
addParticipant:"Account",
addPaymentMethod:"PaymentMethod",
addTrustedAccount:"Branch",
approveModeration:"ApproveModerationResultDTO",
authorizeDecision:"Transaction",
cancelRequest:"Transaction",
capitalAddAuthor:"CapitalProject",
@@ -3660,12 +3736,15 @@ export const ReturnTypes: Record<string,any> = {
notifyOnAnnualGeneralMeet:"MeetAggregate",
processConvertToAxonStatement:"Boolean",
prohibitRequest:"Transaction",
publishPackage:"PublishPackageResultDTO",
publishProjectOfFreeDecision:"Boolean",
publishRelease:"PublishReleaseResultDTO",
publishRequest:"Transaction",
receiveOnRequest:"Transaction",
refresh:"RegisteredAccount",
registerAccount:"RegisteredAccount",
registerParticipant:"Account",
rejectModeration:"RejectModerationResultDTO",
resetKey:"Boolean",
restartAnnualGeneralMeet:"MeetAggregate",
saveReportDraft:"ReportDraft",
@@ -3677,6 +3756,7 @@ export const ReturnTypes: Record<string,any> = {
signBySecretaryOnAnnualGeneralMeet:"MeetAggregate",
startInstall:"StartInstallResult",
startResetKey:"Boolean",
subscribePackage:"SubscribePackageResultDTO",
supplyOnRequest:"Transaction",
triggerNotificationWorkflow:"Boolean",
uninstallExtension:"Boolean",
@@ -4115,9 +4195,22 @@ export const ReturnTypes: Record<string,any> = {
last_name:"String",
middle_name:"String"
},
PublishPackageResultDTO:{
error:"String",
requestId:"String",
status:"String"
},
PublishReleaseResultDTO:{
error:"String",
requestId:"String",
status:"String",
transactionId:"String"
},
Query:{
agreementTemplates:"AgreementTemplate",
agreements:"PaginatedAgreementsPaginationResult",
appsCatalogPendingModerations:"ModerationRequestDTO",
appsCatalogRemotePackages:"AppsCatalogRemotePackageDTO",
buildInitialReportEdits:"BuildInitialReportEdits",
candidates:"PaginatedCandidatesPaginationResult",
capitalCandidates:"PaginatedCapitalCandidatesPaginationResult",
@@ -4272,6 +4365,11 @@ export const ReturnTypes: Record<string,any> = {
requirements:"String",
title:"String"
},
RejectModerationResultDTO:{
error:"String",
requestId:"String",
status:"RejectModerationStatus"
},
ReportCalendarPeriodEntry:{
dueDate:"String",
dueMonth:"Int",
@@ -4432,6 +4530,16 @@ export const ReturnTypes: Record<string,any> = {
action:"ExtendedBlockchainAction",
documentAggregate:"DocumentAggregate"
},
SubscribePackageResultDTO:{
endAt:"String",
error:"String",
freeTrialUsed:"Boolean",
packageId:"String",
plan:"String",
startAt:"String",
state:"SubscriptionStateEnum",
status:"SubscribePackageStatus"
},
SubscriptionStatsDto:{
active:"Int",
inactive:"Int",
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,18 @@
import { client } from 'src/shared/api/client';
import { Mutations } from '@coopenomics/sdk';
import type { IPublishPackageInput, IPublishPackageOutput } from '../model';
async function publishPackage(
data: IPublishPackageInput,
): Promise<IPublishPackageOutput> {
const { [Mutations.Extensions.PublishPackage.name]: result } =
await client.Mutation(Mutations.Extensions.PublishPackage.mutation, {
variables: { data },
});
return result;
}
export const api = {
publishPackage,
};
@@ -0,0 +1,37 @@
import { ref } from 'vue';
import type { Mutations } from '@coopenomics/sdk';
import { api } from '../api';
export type IPublishPackageInput = Mutations.Extensions.PublishPackage.IInput['data'];
export type IPublishPackageOutput = Mutations.Extensions.PublishPackage.IOutput['publishPackage'];
export function usePublishPackage() {
const isSubmitting = ref(false);
const lastResult = ref<IPublishPackageOutput | null>(null);
const lastError = ref<string | null>(null);
async function submit(input: IPublishPackageInput): Promise<IPublishPackageOutput> {
isSubmitting.value = true;
lastError.value = null;
try {
const result = await api.publishPackage(input);
lastResult.value = result;
if (result.status === 'failed') {
lastError.value = result.error || 'Не удалось зарегистрировать пакет';
}
return result;
} catch (e) {
lastError.value = e instanceof Error ? e.message : String(e);
throw e;
} finally {
isSubmitting.value = false;
}
}
return {
isSubmitting,
lastResult,
lastError,
submit,
};
}
@@ -1,26 +1,161 @@
<template>
<q-page padding>
<div class="q-pa-md">
<div class="text-h5 q-mb-md">Опубликовать релиз</div>
<q-banner class="bg-info text-white q-mb-md" rounded>
Заглушка (Epic 9 story 9.3). Здесь будет форма выбора tarball'а
и POST на ca-admin /v1/admin/releases с подписью chairman'а.
</q-banner>
<q-card flat bordered>
<q-card-section>
<div class="text-subtitle2">Шаги публикации</div>
<ol class="text-body2 q-mt-md">
<li>Выбрать пакет из списка «Мои пакеты».</li>
<li>Загрузить tarball на Nexus (через ca-auth presigned PUT).</li>
<li>Отправить manifest + sha256 POST /v1/admin/releases.</li>
<li>Подписать setrelease через chairman key (двух-сиг).</li>
</ol>
</q-card-section>
</q-card>
<div class="q-pa-md" style="max-width: 720px">
<div class="text-h5 q-mb-md">Опубликовать пакет</div>
<BaseBanner v-if="!isChairman" variant="warn" class="q-mb-md">
Регистрация пакета председательская операция.
Сейчас вы не председатель кооператива-разработчика.
</BaseBanner>
<BaseCard variant="default">
<BaseForm :loading="isSubmitting" :error="lastError ?? undefined" @submit="onSubmit">
<BaseInput
v-model="packageId"
label="Идентификатор пакета"
placeholder="@voskhod/demoapp"
hint="Формат @scope/name. scope — Antelope-имя кооператива-разработчика."
:error="errors.packageId"
required
mono
/>
<BaseInput
v-model="ownerUsername"
label="Antelope-аккаунт владельца"
placeholder="voskhod"
hint="1..12 символов: [a-z], [1-5], точка."
:error="errors.ownerUsername"
required
mono
/>
<div>
<div class="t-fs-14 t-fw-500 q-mb-xs">Совместимые подсети</div>
<div class="t-fs-13 text-grey q-mb-sm">
По одному chain_id (64 hex-символа) на строку.
</div>
<q-input
v-model="compatibleSubnetsRaw"
type="textarea"
outlined
dense
autogrow
:error="!!errors.compatibleSubnets"
:error-message="errors.compatibleSubnets"
placeholder="db79c8409645082749ca50640d6f4ee511575acf26c4e2c8e4748e6bf6a01ed4"
input-class="t-mono t-fs-13"
/>
</div>
<template #footer="{ loading }">
<BaseButton
variant="primary"
type="submit"
:loading="loading"
:disabled="!isChairman"
>
Зарегистрировать пакет on-chain
</BaseButton>
</template>
</BaseForm>
</BaseCard>
<BaseBanner
v-if="lastResult && lastResult.status === 'applied'"
variant="pos"
class="q-mt-md"
>
Пакет зарегистрирован. Request: <span class="t-mono">{{ lastResult.requestId }}</span>.
</BaseBanner>
<BaseBanner
v-else-if="lastResult && lastResult.status === 'conflict'"
variant="warn"
class="q-mt-md"
>
Пакет с таким packageId уже зарегистрирован. Используйте «Опубликовать релиз»
для добавления новой версии.
</BaseBanner>
</div>
</q-page>
</template>
<script setup lang="ts">
// Заглушка для V1.
import { computed, reactive, ref } from 'vue';
import {
BaseBanner,
BaseButton,
BaseCard,
BaseForm,
BaseInput,
} from 'src/shared/ui/base';
import { useSessionStore } from 'src/entities/Session';
import {
usePublishPackage,
type IPublishPackageInput,
} from '../features/PublishPackage/model';
const session = useSessionStore();
const isChairman = computed<boolean>(() => {
const role = session.currentUserAccount?.provider_account?.role;
return role === 'chairman';
});
const packageId = ref<string>('');
const ownerUsername = ref<string>(session.username || '');
const compatibleSubnetsRaw = ref<string>('');
const errors = reactive<Record<keyof IPublishPackageInput | 'compatibleSubnets', string>>({
packageId: '',
ownerUsername: '',
compatibleSubnets: '',
});
const { isSubmitting, lastResult, lastError, submit } = usePublishPackage();
const PACKAGE_ID_RE = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
const ANTELOPE_NAME_RE = /^[a-z1-5.]{1,12}$/;
const CHAIN_ID_RE = /^[a-f0-9]{64}$/i;
function validate(): IPublishPackageInput | null {
errors.packageId = '';
errors.ownerUsername = '';
errors.compatibleSubnets = '';
if (!PACKAGE_ID_RE.test(packageId.value.trim())) {
errors.packageId = 'Ожидается @scope/name из [a-z0-9-].';
}
if (!ANTELOPE_NAME_RE.test(ownerUsername.value.trim())) {
errors.ownerUsername = 'Antelope-имя: 1..12 символов из [a-z1-5.].';
}
const subnets = compatibleSubnetsRaw.value
.split(/[\s,]+/)
.map((s) => s.trim().toLowerCase())
.filter(Boolean);
if (subnets.length === 0) {
errors.compatibleSubnets = 'Нужен минимум один chain_id.';
} else {
const bad = subnets.find((s) => !CHAIN_ID_RE.test(s));
if (bad) errors.compatibleSubnets = `Неверный chain_id: ${bad}`;
}
if (errors.packageId || errors.ownerUsername || errors.compatibleSubnets) return null;
return {
packageId: packageId.value.trim(),
ownerUsername: ownerUsername.value.trim(),
compatibleSubnets: subnets,
};
}
async function onSubmit(): Promise<void> {
const input = validate();
if (!input) return;
try {
await submit(input);
} catch {
// ошибка уже легла в lastError через composable
}
}
</script>
@@ -4,3 +4,7 @@ export * as InstallExtension from './installExtension'
export * as UninstallExtension from './uninstallExtension'
/** Обновить расширение */
export * as UpdateExtension from './updateExtension'
/** Зарегистрировать пакет в apps-catalog от имени chairman'а (Story 9.3.b-pub) */
export * as PublishPackage from './publishPackage'
/** Опубликовать релиз пакета с manifest'ом (Story 9.3.b-rel) */
export * as PublishRelease from './publishRelease'
@@ -0,0 +1,25 @@
import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index'
export const name = 'publishPackage'
export const mutation = Selector('Mutation')({
[name]: [
{ data: $('data', 'PublishPackageInputDTO!') },
{
status: true,
requestId: true,
error: true,
},
],
})
export interface IInput {
/**
* @private
*/
[key: string]: unknown
data: ModelTypes['PublishPackageInputDTO']
}
export type IOutput = InputType<GraphQLTypes['Mutation'], typeof mutation>
@@ -0,0 +1,26 @@
import { $, type GraphQLTypes, type InputType, type ModelTypes, Selector } from '../../zeus/index'
export const name = 'publishRelease'
export const mutation = Selector('Mutation')({
[name]: [
{ data: $('data', 'PublishReleaseInputDTO!') },
{
status: true,
requestId: true,
transactionId: true,
error: true,
},
],
})
export interface IInput {
/**
* @private
*/
[key: string]: unknown
data: ModelTypes['PublishReleaseInputDTO']
}
export type IOutput = InputType<GraphQLTypes['Mutation'], typeof mutation>
+95
View File
@@ -99,6 +99,10 @@ export const AllTypesProps: Record<string,any> = {
statuses:"ApprovalStatus"
},
ApprovalStatus: "enum" as const,
ApproveModerationInputDTO:{
scope:"ReleaseScopeInputDTO"
},
ApproveModerationStatus: "enum" as const,
AssetContributionActGenerateDocumentInput:{
},
@@ -631,6 +635,7 @@ export const AllTypesProps: Record<string,any> = {
ModerateRequestInput:{
},
ModerationStatusEnum: "enum" as const,
MoveCapitalIssueToComponentInput:{
},
@@ -647,6 +652,9 @@ export const AllTypesProps: Record<string,any> = {
addTrustedAccount:{
data:"AddTrustedAccountInput"
},
approveModeration:{
data:"ApproveModerationInputDTO"
},
authorizeDecision:{
data:"AuthorizeDecisionInput"
},
@@ -1136,9 +1144,15 @@ export const AllTypesProps: Record<string,any> = {
prohibitRequest:{
data:"ProhibitRequestInput"
},
publishPackage:{
data:"PublishPackageInputDTO"
},
publishProjectOfFreeDecision:{
data:"PublishProjectFreeDecisionInput"
},
publishRelease:{
data:"PublishReleaseInputDTO"
},
publishRequest:{
data:"PublishRequestInput"
},
@@ -1154,6 +1168,9 @@ export const AllTypesProps: Record<string,any> = {
registerParticipant:{
data:"RegisterParticipantInput"
},
rejectModeration:{
data:"RejectModerationInputDTO"
},
resetKey:{
data:"ResetKeyInput"
},
@@ -1187,6 +1204,9 @@ export const AllTypesProps: Record<string,any> = {
startResetKey:{
data:"StartResetKeyInput"
},
subscribePackage:{
data:"SubscribePackageInputDTO"
},
supplyOnRequest:{
data:"SupplyOnRequestInput"
},
@@ -1321,9 +1341,15 @@ export const AllTypesProps: Record<string,any> = {
},
ProjectStatus: "enum" as const,
PublishPackageInputDTO:{
},
PublishProjectFreeDecisionInput:{
document:"ProjectFreeDecisionSignedDocumentInput"
},
PublishReleaseInputDTO:{
manifest:"JSON"
},
PublishRequestInput:{
},
@@ -1338,6 +1364,9 @@ export const AllTypesProps: Record<string,any> = {
filter:"AgreementFilter",
options:"PaginationInput"
},
appsCatalogPendingModerations:{
status:"ModerationStatusEnum"
},
appsCatalogRemotePackages:{
},
@@ -1664,6 +1693,15 @@ export const AllTypesProps: Record<string,any> = {
user_agreement:"SignedDigitalDocumentInput",
wallet_agreement:"SignedDigitalDocumentInput"
},
RejectModerationInputDTO:{
},
RejectModerationStatus: "enum" as const,
ReleaseScopeInputDTO:{
type:"ReleaseScopeType"
},
ReleaseScopeType: "enum" as const,
ReleaseTypeEnum: "enum" as const,
RemoveSecretaryRoomInput:{
},
@@ -1836,6 +1874,11 @@ export const AllTypesProps: Record<string,any> = {
SubmitVoteInput:{
votes:"VoteDistributionInput"
},
SubscribePackageInputDTO:{
},
SubscribePackageStatus: "enum" as const,
SubscriptionStateEnum: "enum" as const,
SupplyOnRequestInput:{
document:"AssetContributionActSignedDocumentInput"
},
@@ -2043,6 +2086,13 @@ export const ReturnTypes: Record<string,any> = {
status:"ApprovalStatus",
username:"String"
},
ApproveModerationResultDTO:{
error:"String",
packageId:"String",
requestId:"String",
status:"ApproveModerationStatus",
version:"String"
},
AppsCatalogRemotePackageDTO:{
compatibleSubnets:"String",
description:"String",
@@ -3502,6 +3552,19 @@ export const ReturnTypes: Record<string,any> = {
reason:"String",
source:"RequisiteSource"
},
ModerationRequestDTO:{
brief:"String",
id:"String",
packageId:"String",
releaseType:"ReleaseTypeEnum",
requiresOverride:"Boolean",
scope:"JSON",
status:"ModerationStatusEnum",
submittedAt:"String",
submittedBy:"String",
updatedAt:"String",
version:"String"
},
MonoAccount:{
email:"String",
has_account:"Boolean",
@@ -3523,6 +3586,7 @@ export const ReturnTypes: Record<string,any> = {
addParticipant:"Account",
addPaymentMethod:"PaymentMethod",
addTrustedAccount:"Branch",
approveModeration:"ApproveModerationResultDTO",
authorizeDecision:"Transaction",
cancelRequest:"Transaction",
capitalAddAuthor:"CapitalProject",
@@ -3672,12 +3736,15 @@ export const ReturnTypes: Record<string,any> = {
notifyOnAnnualGeneralMeet:"MeetAggregate",
processConvertToAxonStatement:"Boolean",
prohibitRequest:"Transaction",
publishPackage:"PublishPackageResultDTO",
publishProjectOfFreeDecision:"Boolean",
publishRelease:"PublishReleaseResultDTO",
publishRequest:"Transaction",
receiveOnRequest:"Transaction",
refresh:"RegisteredAccount",
registerAccount:"RegisteredAccount",
registerParticipant:"Account",
rejectModeration:"RejectModerationResultDTO",
resetKey:"Boolean",
restartAnnualGeneralMeet:"MeetAggregate",
saveReportDraft:"ReportDraft",
@@ -3689,6 +3756,7 @@ export const ReturnTypes: Record<string,any> = {
signBySecretaryOnAnnualGeneralMeet:"MeetAggregate",
startInstall:"StartInstallResult",
startResetKey:"Boolean",
subscribePackage:"SubscribePackageResultDTO",
supplyOnRequest:"Transaction",
triggerNotificationWorkflow:"Boolean",
uninstallExtension:"Boolean",
@@ -4127,9 +4195,21 @@ export const ReturnTypes: Record<string,any> = {
last_name:"String",
middle_name:"String"
},
PublishPackageResultDTO:{
error:"String",
requestId:"String",
status:"String"
},
PublishReleaseResultDTO:{
error:"String",
requestId:"String",
status:"String",
transactionId:"String"
},
Query:{
agreementTemplates:"AgreementTemplate",
agreements:"PaginatedAgreementsPaginationResult",
appsCatalogPendingModerations:"ModerationRequestDTO",
appsCatalogRemotePackages:"AppsCatalogRemotePackageDTO",
buildInitialReportEdits:"BuildInitialReportEdits",
candidates:"PaginatedCandidatesPaginationResult",
@@ -4285,6 +4365,11 @@ export const ReturnTypes: Record<string,any> = {
requirements:"String",
title:"String"
},
RejectModerationResultDTO:{
error:"String",
requestId:"String",
status:"RejectModerationStatus"
},
ReportCalendarPeriodEntry:{
dueDate:"String",
dueMonth:"Int",
@@ -4445,6 +4530,16 @@ export const ReturnTypes: Record<string,any> = {
action:"ExtendedBlockchainAction",
documentAggregate:"DocumentAggregate"
},
SubscribePackageResultDTO:{
endAt:"String",
error:"String",
freeTrialUsed:"Boolean",
packageId:"String",
plan:"String",
startAt:"String",
state:"SubscriptionStateEnum",
status:"SubscribePackageStatus"
},
SubscriptionStatsDto:{
active:"Int",
inactive:"Int",
File diff suppressed because it is too large Load Diff