sa2kit 1.6.85 → 1.6.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boothVaultService-Cn4WPhjg.d.mts +83 -0
- package/dist/boothVaultService-Cn4WPhjg.d.ts +83 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +366 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -2
- package/dist/index.mjs.map +1 -1
- package/dist/vocaloidBooth/index.d.mts +64 -0
- package/dist/vocaloidBooth/index.d.ts +64 -0
- package/dist/vocaloidBooth/index.js +376 -0
- package/dist/vocaloidBooth/index.js.map +1 -0
- package/dist/vocaloidBooth/index.mjs +362 -0
- package/dist/vocaloidBooth/index.mjs.map +1 -0
- package/dist/vocaloidBooth/server/index.d.mts +110 -0
- package/dist/vocaloidBooth/server/index.d.ts +110 -0
- package/dist/vocaloidBooth/server/index.js +247 -0
- package/dist/vocaloidBooth/server/index.js.map +1 -0
- package/dist/vocaloidBooth/server/index.mjs +237 -0
- package/dist/vocaloidBooth/server/index.mjs.map +1 -0
- package/dist/vocaloidBooth/web/index.d.mts +3 -0
- package/dist/vocaloidBooth/web/index.d.ts +3 -0
- package/dist/vocaloidBooth/web/index.js +376 -0
- package/dist/vocaloidBooth/web/index.js.map +1 -0
- package/dist/vocaloidBooth/web/index.mjs +362 -0
- package/dist/vocaloidBooth/web/index.mjs.map +1 -0
- package/package.json +16 -1
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
type BoothFileKind = 'project' | 'output' | 'asset' | 'other';
|
|
2
|
+
interface BoothFileItem {
|
|
3
|
+
id: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
size: number;
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
objectKey: string;
|
|
8
|
+
checksum?: string;
|
|
9
|
+
kind?: BoothFileKind;
|
|
10
|
+
}
|
|
11
|
+
interface BoothUploadRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
matchCode: string;
|
|
14
|
+
boothId: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
files: BoothFileItem[];
|
|
18
|
+
metadata?: {
|
|
19
|
+
nickname?: string;
|
|
20
|
+
contactTail?: string;
|
|
21
|
+
note?: string;
|
|
22
|
+
};
|
|
23
|
+
status: 'active' | 'expired' | 'deleted';
|
|
24
|
+
downloadCount: number;
|
|
25
|
+
}
|
|
26
|
+
type BoothAuditEventType = 'upload.created' | 'redeem.success' | 'redeem.failed' | 'redeem.blocked' | 'record.expired';
|
|
27
|
+
interface BoothAuditEvent {
|
|
28
|
+
type: BoothAuditEventType;
|
|
29
|
+
at: string;
|
|
30
|
+
boothId?: string;
|
|
31
|
+
recordId?: string;
|
|
32
|
+
matchCode?: string;
|
|
33
|
+
requesterKey?: string;
|
|
34
|
+
detail?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
interface CreateBoothUploadInput {
|
|
37
|
+
boothId: string;
|
|
38
|
+
ttlHours?: number;
|
|
39
|
+
metadata?: BoothUploadRecord['metadata'];
|
|
40
|
+
files: Omit<BoothFileItem, 'id'>[];
|
|
41
|
+
}
|
|
42
|
+
interface CreateBoothUploadResult {
|
|
43
|
+
record: BoothUploadRecord;
|
|
44
|
+
downloadUrlPath: string;
|
|
45
|
+
}
|
|
46
|
+
interface BoothVaultStore {
|
|
47
|
+
saveRecord(record: BoothUploadRecord): Promise<void>;
|
|
48
|
+
findByMatchCode(matchCode: string): Promise<BoothUploadRecord | null>;
|
|
49
|
+
findByRecordId?(recordId: string): Promise<BoothUploadRecord | null>;
|
|
50
|
+
incrementDownloadCount(recordId: string): Promise<void>;
|
|
51
|
+
existsByMatchCode(matchCode: string): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface BoothRedeemGuardLike {
|
|
55
|
+
assertAllowed(subjectKey: string): void;
|
|
56
|
+
registerAttempt(subjectKey: string, success: boolean): void;
|
|
57
|
+
}
|
|
58
|
+
interface BoothVaultServiceOptions {
|
|
59
|
+
store: BoothVaultStore;
|
|
60
|
+
codeLength?: number;
|
|
61
|
+
defaultTtlHours?: number;
|
|
62
|
+
baseDownloadPath?: string;
|
|
63
|
+
redeemGuard?: BoothRedeemGuardLike;
|
|
64
|
+
onAuditEvent?: (event: BoothAuditEvent) => void;
|
|
65
|
+
}
|
|
66
|
+
declare class BoothVaultService {
|
|
67
|
+
private emitAudit;
|
|
68
|
+
private readonly store;
|
|
69
|
+
private readonly codeLength;
|
|
70
|
+
private readonly defaultTtlHours;
|
|
71
|
+
private readonly baseDownloadPath;
|
|
72
|
+
private readonly redeemGuard?;
|
|
73
|
+
private readonly onAuditEvent?;
|
|
74
|
+
constructor(options: BoothVaultServiceOptions);
|
|
75
|
+
createUpload(input: CreateBoothUploadInput): Promise<CreateBoothUploadResult>;
|
|
76
|
+
getByMatchCode(matchCode: string): Promise<BoothUploadRecord | null>;
|
|
77
|
+
markDownloaded(recordId: string): Promise<void>;
|
|
78
|
+
resolveDownloadFilesByCode(matchCode: string, options?: {
|
|
79
|
+
requesterKey?: string;
|
|
80
|
+
}): Promise<BoothUploadRecord | null>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { type BoothVaultStore as B, type CreateBoothUploadInput as C, type BoothUploadRecord as a, type BoothFileItem as b, type BoothFileKind as c, BoothVaultService as d, type CreateBoothUploadResult as e, type BoothAuditEvent as f, type BoothAuditEventType as g, type BoothRedeemGuardLike as h, type BoothVaultServiceOptions as i };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
type BoothFileKind = 'project' | 'output' | 'asset' | 'other';
|
|
2
|
+
interface BoothFileItem {
|
|
3
|
+
id: string;
|
|
4
|
+
fileName: string;
|
|
5
|
+
size: number;
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
objectKey: string;
|
|
8
|
+
checksum?: string;
|
|
9
|
+
kind?: BoothFileKind;
|
|
10
|
+
}
|
|
11
|
+
interface BoothUploadRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
matchCode: string;
|
|
14
|
+
boothId: string;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
expiresAt: string;
|
|
17
|
+
files: BoothFileItem[];
|
|
18
|
+
metadata?: {
|
|
19
|
+
nickname?: string;
|
|
20
|
+
contactTail?: string;
|
|
21
|
+
note?: string;
|
|
22
|
+
};
|
|
23
|
+
status: 'active' | 'expired' | 'deleted';
|
|
24
|
+
downloadCount: number;
|
|
25
|
+
}
|
|
26
|
+
type BoothAuditEventType = 'upload.created' | 'redeem.success' | 'redeem.failed' | 'redeem.blocked' | 'record.expired';
|
|
27
|
+
interface BoothAuditEvent {
|
|
28
|
+
type: BoothAuditEventType;
|
|
29
|
+
at: string;
|
|
30
|
+
boothId?: string;
|
|
31
|
+
recordId?: string;
|
|
32
|
+
matchCode?: string;
|
|
33
|
+
requesterKey?: string;
|
|
34
|
+
detail?: Record<string, unknown>;
|
|
35
|
+
}
|
|
36
|
+
interface CreateBoothUploadInput {
|
|
37
|
+
boothId: string;
|
|
38
|
+
ttlHours?: number;
|
|
39
|
+
metadata?: BoothUploadRecord['metadata'];
|
|
40
|
+
files: Omit<BoothFileItem, 'id'>[];
|
|
41
|
+
}
|
|
42
|
+
interface CreateBoothUploadResult {
|
|
43
|
+
record: BoothUploadRecord;
|
|
44
|
+
downloadUrlPath: string;
|
|
45
|
+
}
|
|
46
|
+
interface BoothVaultStore {
|
|
47
|
+
saveRecord(record: BoothUploadRecord): Promise<void>;
|
|
48
|
+
findByMatchCode(matchCode: string): Promise<BoothUploadRecord | null>;
|
|
49
|
+
findByRecordId?(recordId: string): Promise<BoothUploadRecord | null>;
|
|
50
|
+
incrementDownloadCount(recordId: string): Promise<void>;
|
|
51
|
+
existsByMatchCode(matchCode: string): Promise<boolean>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface BoothRedeemGuardLike {
|
|
55
|
+
assertAllowed(subjectKey: string): void;
|
|
56
|
+
registerAttempt(subjectKey: string, success: boolean): void;
|
|
57
|
+
}
|
|
58
|
+
interface BoothVaultServiceOptions {
|
|
59
|
+
store: BoothVaultStore;
|
|
60
|
+
codeLength?: number;
|
|
61
|
+
defaultTtlHours?: number;
|
|
62
|
+
baseDownloadPath?: string;
|
|
63
|
+
redeemGuard?: BoothRedeemGuardLike;
|
|
64
|
+
onAuditEvent?: (event: BoothAuditEvent) => void;
|
|
65
|
+
}
|
|
66
|
+
declare class BoothVaultService {
|
|
67
|
+
private emitAudit;
|
|
68
|
+
private readonly store;
|
|
69
|
+
private readonly codeLength;
|
|
70
|
+
private readonly defaultTtlHours;
|
|
71
|
+
private readonly baseDownloadPath;
|
|
72
|
+
private readonly redeemGuard?;
|
|
73
|
+
private readonly onAuditEvent?;
|
|
74
|
+
constructor(options: BoothVaultServiceOptions);
|
|
75
|
+
createUpload(input: CreateBoothUploadInput): Promise<CreateBoothUploadResult>;
|
|
76
|
+
getByMatchCode(matchCode: string): Promise<BoothUploadRecord | null>;
|
|
77
|
+
markDownloaded(recordId: string): Promise<void>;
|
|
78
|
+
resolveDownloadFilesByCode(matchCode: string, options?: {
|
|
79
|
+
requesterKey?: string;
|
|
80
|
+
}): Promise<BoothUploadRecord | null>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export { type BoothVaultStore as B, type CreateBoothUploadInput as C, type BoothUploadRecord as a, type BoothFileItem as b, type BoothFileKind as c, BoothVaultService as d, type CreateBoothUploadResult as e, type BoothAuditEvent as f, type BoothAuditEventType as g, type BoothRedeemGuardLike as h, type BoothVaultServiceOptions as i };
|
package/dist/index.d.mts
CHANGED
|
@@ -18,6 +18,8 @@ export { u as useFestivalCardConfig } from './useFestivalCardConfig-DuiT7yuB.mjs
|
|
|
18
18
|
export { FestivalCardBook3D, FestivalCardBook3DProps, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardStudio } from './festivalCard/web/index.mjs';
|
|
19
19
|
export { F as FestivalCardService, c as createInMemoryFestivalCardDb } from './festivalCardService-Bsx2GfGI.mjs';
|
|
20
20
|
export { j as FestivalCardAudioConfig, F as FestivalCardConfig, b as FestivalCardConfigSummary, c as FestivalCardDbAdapter, h as FestivalCardElement, e as FestivalCardElementBase, d as FestivalCardElementType, g as FestivalCardImageElement, i as FestivalCardPage, a as FestivalCardServiceOptions, f as FestivalCardTextElement } from './types-DBdb0jfs.mjs';
|
|
21
|
+
export { f as BoothAuditEvent, g as BoothAuditEventType, b as BoothFileItem, c as BoothFileKind, h as BoothRedeemGuardLike, a as BoothUploadRecord, d as BoothVaultService, i as BoothVaultServiceOptions, B as BoothVaultStore, C as CreateBoothUploadInput, e as CreateBoothUploadResult } from './boothVaultService-Cn4WPhjg.mjs';
|
|
22
|
+
export { BoothConfigPage, BoothConfigPageProps, BoothRedeemPanel, BoothRedeemPanelProps, BoothSuccessCard, BoothSuccessCardProps, BoothUploadPanel, BoothUploadPanelProps, BoothUploadSubmitPayload, GenerateMatchCodeOptions, VocaloidBoothConfig, defaultVocaloidBoothConfig, generateMatchCode, normalizeMatchCode, normalizeVocaloidBoothConfig } from './vocaloidBooth/index.mjs';
|
|
21
23
|
export { S as StorageAdapter, a as StorageChangeEvent } from './types-BaZccpvk.mjs';
|
|
22
24
|
export { b as useAsyncStorage, d as useElectronStorage, a as useLocalStorage, u as useStorage, c as useTaroStorage } from './useElectronStorage-Dj0rcorG.mjs';
|
|
23
25
|
import './types-CbTsi9CZ.mjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export { u as useFestivalCardConfig } from './useFestivalCardConfig-BRbfBEv5.js'
|
|
|
18
18
|
export { FestivalCardBook3D, FestivalCardBook3DProps, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardStudio } from './festivalCard/web/index.js';
|
|
19
19
|
export { F as FestivalCardService, c as createInMemoryFestivalCardDb } from './festivalCardService-C58MuCpF.js';
|
|
20
20
|
export { j as FestivalCardAudioConfig, F as FestivalCardConfig, b as FestivalCardConfigSummary, c as FestivalCardDbAdapter, h as FestivalCardElement, e as FestivalCardElementBase, d as FestivalCardElementType, g as FestivalCardImageElement, i as FestivalCardPage, a as FestivalCardServiceOptions, f as FestivalCardTextElement } from './types-DBdb0jfs.js';
|
|
21
|
+
export { f as BoothAuditEvent, g as BoothAuditEventType, b as BoothFileItem, c as BoothFileKind, h as BoothRedeemGuardLike, a as BoothUploadRecord, d as BoothVaultService, i as BoothVaultServiceOptions, B as BoothVaultStore, C as CreateBoothUploadInput, e as CreateBoothUploadResult } from './boothVaultService-Cn4WPhjg.js';
|
|
22
|
+
export { BoothConfigPage, BoothConfigPageProps, BoothRedeemPanel, BoothRedeemPanelProps, BoothSuccessCard, BoothSuccessCardProps, BoothUploadPanel, BoothUploadPanelProps, BoothUploadSubmitPayload, GenerateMatchCodeOptions, VocaloidBoothConfig, defaultVocaloidBoothConfig, generateMatchCode, normalizeMatchCode, normalizeVocaloidBoothConfig } from './vocaloidBooth/index.js';
|
|
21
23
|
export { S as StorageAdapter, a as StorageChangeEvent } from './types-BaZccpvk.js';
|
|
22
24
|
export { b as useAsyncStorage, d as useElectronStorage, a as useLocalStorage, u as useStorage, c as useTaroStorage } from './useElectronStorage-DwnNfIhl.js';
|
|
23
25
|
import './types-CbTsi9CZ.js';
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var sortable = require('@dnd-kit/sortable');
|
|
|
14
14
|
var utilities = require('@dnd-kit/utilities');
|
|
15
15
|
var pgCore = require('drizzle-orm/pg-core');
|
|
16
16
|
var drizzleOrm = require('drizzle-orm');
|
|
17
|
-
require('crypto');
|
|
17
|
+
var crypto = require('crypto');
|
|
18
18
|
require('bcryptjs');
|
|
19
19
|
require('jsonwebtoken');
|
|
20
20
|
var THREE2 = require('three');
|
|
@@ -8038,6 +8038,362 @@ var createInMemoryFestivalCardDb = () => ({
|
|
|
8038
8038
|
}
|
|
8039
8039
|
});
|
|
8040
8040
|
|
|
8041
|
+
// src/vocaloidBooth/core/code.ts
|
|
8042
|
+
var AMBIGUOUS = /* @__PURE__ */ new Set(["0", "1", "I", "O", "L"]);
|
|
8043
|
+
var ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789".split("").filter((c) => !AMBIGUOUS.has(c));
|
|
8044
|
+
var normalizeMatchCode = (value) => value.trim().toUpperCase();
|
|
8045
|
+
var generateMatchCode = async ({
|
|
8046
|
+
length = 6,
|
|
8047
|
+
maxAttempts = 20,
|
|
8048
|
+
exists
|
|
8049
|
+
}) => {
|
|
8050
|
+
if (length < 4) {
|
|
8051
|
+
throw new Error("Match code length must be at least 4");
|
|
8052
|
+
}
|
|
8053
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
8054
|
+
const code = Array.from({ length }).map(() => ALPHABET[Math.floor(Math.random() * ALPHABET.length)]).join("");
|
|
8055
|
+
if (!await exists(code)) {
|
|
8056
|
+
return code;
|
|
8057
|
+
}
|
|
8058
|
+
}
|
|
8059
|
+
throw new Error("Unable to generate unique match code");
|
|
8060
|
+
};
|
|
8061
|
+
var BoothVaultService = class {
|
|
8062
|
+
emitAudit(event) {
|
|
8063
|
+
this.onAuditEvent?.({
|
|
8064
|
+
...event,
|
|
8065
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
8066
|
+
});
|
|
8067
|
+
}
|
|
8068
|
+
constructor(options) {
|
|
8069
|
+
this.store = options.store;
|
|
8070
|
+
this.codeLength = options.codeLength ?? 6;
|
|
8071
|
+
this.defaultTtlHours = options.defaultTtlHours ?? 24 * 14;
|
|
8072
|
+
this.baseDownloadPath = options.baseDownloadPath ?? "/redeem";
|
|
8073
|
+
this.redeemGuard = options.redeemGuard;
|
|
8074
|
+
this.onAuditEvent = options.onAuditEvent;
|
|
8075
|
+
}
|
|
8076
|
+
async createUpload(input) {
|
|
8077
|
+
if (!input.files?.length) {
|
|
8078
|
+
throw new Error("At least one file is required");
|
|
8079
|
+
}
|
|
8080
|
+
const now = /* @__PURE__ */ new Date();
|
|
8081
|
+
const ttlHours = Math.max(1, input.ttlHours ?? this.defaultTtlHours);
|
|
8082
|
+
const expiresAt = new Date(now.getTime() + ttlHours * 60 * 60 * 1e3);
|
|
8083
|
+
const matchCode = await generateMatchCode({
|
|
8084
|
+
length: this.codeLength,
|
|
8085
|
+
exists: (code) => this.store.existsByMatchCode(code)
|
|
8086
|
+
});
|
|
8087
|
+
const record = {
|
|
8088
|
+
id: crypto.randomUUID(),
|
|
8089
|
+
boothId: input.boothId,
|
|
8090
|
+
matchCode,
|
|
8091
|
+
createdAt: now.toISOString(),
|
|
8092
|
+
expiresAt: expiresAt.toISOString(),
|
|
8093
|
+
files: input.files.map((file) => ({
|
|
8094
|
+
...file,
|
|
8095
|
+
id: crypto.randomUUID()
|
|
8096
|
+
})),
|
|
8097
|
+
metadata: input.metadata,
|
|
8098
|
+
status: "active",
|
|
8099
|
+
downloadCount: 0
|
|
8100
|
+
};
|
|
8101
|
+
await this.store.saveRecord(record);
|
|
8102
|
+
this.emitAudit({
|
|
8103
|
+
type: "upload.created",
|
|
8104
|
+
boothId: record.boothId,
|
|
8105
|
+
recordId: record.id,
|
|
8106
|
+
matchCode: record.matchCode,
|
|
8107
|
+
detail: { fileCount: record.files.length }
|
|
8108
|
+
});
|
|
8109
|
+
return {
|
|
8110
|
+
record,
|
|
8111
|
+
downloadUrlPath: `${this.baseDownloadPath}?code=${record.matchCode}`
|
|
8112
|
+
};
|
|
8113
|
+
}
|
|
8114
|
+
async getByMatchCode(matchCode) {
|
|
8115
|
+
const normalized = normalizeMatchCode(matchCode);
|
|
8116
|
+
const record = await this.store.findByMatchCode(normalized);
|
|
8117
|
+
if (!record) {
|
|
8118
|
+
return null;
|
|
8119
|
+
}
|
|
8120
|
+
if (new Date(record.expiresAt).getTime() <= Date.now() && record.status === "active") {
|
|
8121
|
+
return {
|
|
8122
|
+
...record,
|
|
8123
|
+
status: "expired"
|
|
8124
|
+
};
|
|
8125
|
+
}
|
|
8126
|
+
return record;
|
|
8127
|
+
}
|
|
8128
|
+
async markDownloaded(recordId) {
|
|
8129
|
+
await this.store.incrementDownloadCount(recordId);
|
|
8130
|
+
}
|
|
8131
|
+
async resolveDownloadFilesByCode(matchCode, options) {
|
|
8132
|
+
const requesterKey = options?.requesterKey;
|
|
8133
|
+
if (requesterKey && this.redeemGuard) {
|
|
8134
|
+
try {
|
|
8135
|
+
this.redeemGuard.assertAllowed(requesterKey);
|
|
8136
|
+
} catch (error) {
|
|
8137
|
+
this.emitAudit({
|
|
8138
|
+
type: "redeem.blocked",
|
|
8139
|
+
requesterKey,
|
|
8140
|
+
matchCode,
|
|
8141
|
+
detail: { message: error instanceof Error ? error.message : "blocked" }
|
|
8142
|
+
});
|
|
8143
|
+
throw error;
|
|
8144
|
+
}
|
|
8145
|
+
}
|
|
8146
|
+
const record = await this.getByMatchCode(matchCode);
|
|
8147
|
+
const success = !!record && record.status === "active";
|
|
8148
|
+
if (requesterKey && this.redeemGuard) {
|
|
8149
|
+
this.redeemGuard.registerAttempt(requesterKey, success);
|
|
8150
|
+
}
|
|
8151
|
+
if (!success) {
|
|
8152
|
+
this.emitAudit({
|
|
8153
|
+
type: "redeem.failed",
|
|
8154
|
+
requesterKey,
|
|
8155
|
+
matchCode,
|
|
8156
|
+
boothId: record?.boothId,
|
|
8157
|
+
recordId: record?.id
|
|
8158
|
+
});
|
|
8159
|
+
return record;
|
|
8160
|
+
}
|
|
8161
|
+
await this.markDownloaded(record.id);
|
|
8162
|
+
const reloaded = this.store.findByRecordId ? await this.store.findByRecordId(record.id) : await this.getByMatchCode(record.matchCode);
|
|
8163
|
+
this.emitAudit({
|
|
8164
|
+
type: "redeem.success",
|
|
8165
|
+
requesterKey,
|
|
8166
|
+
matchCode: record.matchCode,
|
|
8167
|
+
boothId: record.boothId,
|
|
8168
|
+
recordId: record.id
|
|
8169
|
+
});
|
|
8170
|
+
return reloaded ?? record;
|
|
8171
|
+
}
|
|
8172
|
+
};
|
|
8173
|
+
|
|
8174
|
+
// src/vocaloidBooth/core/config.ts
|
|
8175
|
+
var defaultVocaloidBoothConfig = {
|
|
8176
|
+
boothId: "default-booth",
|
|
8177
|
+
title: "MMD / Vocaloid \u521B\u4F5C\u6587\u4EF6\u5BC4\u5B58\u7AD9",
|
|
8178
|
+
description: "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6\u5E76\u751F\u6210\u5339\u914D\u7801\uFF0C\u540E\u7EED\u53EF\u51ED\u7801\u4E0B\u8F7D",
|
|
8179
|
+
defaultTtlHours: 24 * 14,
|
|
8180
|
+
maxFiles: 20,
|
|
8181
|
+
maxSingleFileSizeMb: 2048,
|
|
8182
|
+
maxTotalFileSizeMb: 5120,
|
|
8183
|
+
allowedExtensions: ["zip", "7z", "rar", "vsqx", "vpr", "vmd", "pmx", "wav", "mp3", "mp4"]
|
|
8184
|
+
};
|
|
8185
|
+
var normalizeVocaloidBoothConfig = (input) => {
|
|
8186
|
+
const merged = {
|
|
8187
|
+
...defaultVocaloidBoothConfig,
|
|
8188
|
+
...input ?? {}
|
|
8189
|
+
};
|
|
8190
|
+
return {
|
|
8191
|
+
...merged,
|
|
8192
|
+
boothId: merged.boothId || defaultVocaloidBoothConfig.boothId,
|
|
8193
|
+
title: merged.title || defaultVocaloidBoothConfig.title,
|
|
8194
|
+
defaultTtlHours: Math.max(1, merged.defaultTtlHours),
|
|
8195
|
+
maxFiles: Math.max(1, merged.maxFiles),
|
|
8196
|
+
maxSingleFileSizeMb: Math.max(1, merged.maxSingleFileSizeMb),
|
|
8197
|
+
maxTotalFileSizeMb: Math.max(1, merged.maxTotalFileSizeMb),
|
|
8198
|
+
allowedExtensions: (merged.allowedExtensions?.length ? merged.allowedExtensions : defaultVocaloidBoothConfig.allowedExtensions).map((ext) => ext.toLowerCase())
|
|
8199
|
+
};
|
|
8200
|
+
};
|
|
8201
|
+
var BoothUploadPanel = ({
|
|
8202
|
+
boothId,
|
|
8203
|
+
maxFiles = 10,
|
|
8204
|
+
maxFileSizeMb = 2048,
|
|
8205
|
+
accept,
|
|
8206
|
+
uploading = false,
|
|
8207
|
+
onSubmit
|
|
8208
|
+
}) => {
|
|
8209
|
+
const [files, setFiles] = React69.useState([]);
|
|
8210
|
+
const [nickname, setNickname] = React69.useState("");
|
|
8211
|
+
const [contactTail, setContactTail] = React69.useState("");
|
|
8212
|
+
const [ttlHours, setTtlHours] = React69.useState(24 * 14);
|
|
8213
|
+
const [error, setError] = React69.useState(null);
|
|
8214
|
+
const totalSizeMb = React69.useMemo(
|
|
8215
|
+
() => files.reduce((acc, file) => acc + file.size, 0) / 1024 / 1024,
|
|
8216
|
+
[files]
|
|
8217
|
+
);
|
|
8218
|
+
const addFiles = (newFiles) => {
|
|
8219
|
+
if (!newFiles) return;
|
|
8220
|
+
const incoming = Array.from(newFiles);
|
|
8221
|
+
const next = [...files, ...incoming];
|
|
8222
|
+
if (next.length > maxFiles) {
|
|
8223
|
+
setError(`\u6700\u591A\u4E0A\u4F20 ${maxFiles} \u4E2A\u6587\u4EF6`);
|
|
8224
|
+
return;
|
|
8225
|
+
}
|
|
8226
|
+
const oversized = incoming.find((f) => f.size > maxFileSizeMb * 1024 * 1024);
|
|
8227
|
+
if (oversized) {
|
|
8228
|
+
setError(`\u6587\u4EF6 ${oversized.name} \u8D85\u8FC7 ${maxFileSizeMb}MB \u9650\u5236`);
|
|
8229
|
+
return;
|
|
8230
|
+
}
|
|
8231
|
+
setError(null);
|
|
8232
|
+
setFiles(next);
|
|
8233
|
+
};
|
|
8234
|
+
const removeFile = (name) => setFiles((prev) => prev.filter((f) => f.name !== name));
|
|
8235
|
+
const handleSubmit = async () => {
|
|
8236
|
+
if (files.length === 0) {
|
|
8237
|
+
setError("\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u6587\u4EF6");
|
|
8238
|
+
return;
|
|
8239
|
+
}
|
|
8240
|
+
setError(null);
|
|
8241
|
+
await onSubmit({
|
|
8242
|
+
boothId,
|
|
8243
|
+
files,
|
|
8244
|
+
nickname: nickname || void 0,
|
|
8245
|
+
contactTail: contactTail || void 0,
|
|
8246
|
+
ttlHours
|
|
8247
|
+
});
|
|
8248
|
+
};
|
|
8249
|
+
return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React69__namespace.default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6"), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8250
|
+
"input",
|
|
8251
|
+
{
|
|
8252
|
+
type: "file",
|
|
8253
|
+
multiple: true,
|
|
8254
|
+
accept,
|
|
8255
|
+
onChange: (e) => addFiles(e.target.files),
|
|
8256
|
+
className: "mb-3 block w-full text-sm"
|
|
8257
|
+
}
|
|
8258
|
+
), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 grid grid-cols-1 gap-2 md:grid-cols-3" }, /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8259
|
+
"input",
|
|
8260
|
+
{
|
|
8261
|
+
value: nickname,
|
|
8262
|
+
onChange: (e) => setNickname(e.target.value),
|
|
8263
|
+
placeholder: "\u6635\u79F0\uFF08\u53EF\u9009\uFF09",
|
|
8264
|
+
className: "rounded-md border px-3 py-2 text-sm"
|
|
8265
|
+
}
|
|
8266
|
+
), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8267
|
+
"input",
|
|
8268
|
+
{
|
|
8269
|
+
value: contactTail,
|
|
8270
|
+
onChange: (e) => setContactTail(e.target.value),
|
|
8271
|
+
placeholder: "\u8054\u7CFB\u65B9\u5F0F\u540E4\u4F4D\uFF08\u53EF\u9009\uFF09",
|
|
8272
|
+
className: "rounded-md border px-3 py-2 text-sm"
|
|
8273
|
+
}
|
|
8274
|
+
), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8275
|
+
"input",
|
|
8276
|
+
{
|
|
8277
|
+
value: ttlHours,
|
|
8278
|
+
type: "number",
|
|
8279
|
+
min: 1,
|
|
8280
|
+
onChange: (e) => setTtlHours(Number(e.target.value) || 24),
|
|
8281
|
+
placeholder: "\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09",
|
|
8282
|
+
className: "rounded-md border px-3 py-2 text-sm"
|
|
8283
|
+
}
|
|
8284
|
+
)), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 text-xs text-slate-500" }, "\u5DF2\u9009 ", files.length, " \u4E2A\u6587\u4EF6\uFF0C\u603B\u8BA1 ", totalSizeMb.toFixed(2), " MB"), /* @__PURE__ */ React69__namespace.default.createElement("ul", { className: "mb-3 max-h-40 overflow-auto rounded-md border border-slate-100 p-2 text-sm" }, files.length === 0 && /* @__PURE__ */ React69__namespace.default.createElement("li", { className: "text-slate-400" }, "\u5C1A\u672A\u9009\u62E9\u6587\u4EF6"), files.map((file) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: `${file.name}-${file.size}`, className: "mb-1 flex items-center justify-between gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement("span", { className: "truncate" }, file.name), /* @__PURE__ */ React69__namespace.default.createElement("button", { type: "button", className: "text-rose-500", onClick: () => removeFile(file.name) }, "\u79FB\u9664")))), error && /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 rounded-md bg-rose-50 p-2 text-sm text-rose-700" }, error), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8285
|
+
"button",
|
|
8286
|
+
{
|
|
8287
|
+
type: "button",
|
|
8288
|
+
disabled: uploading,
|
|
8289
|
+
onClick: handleSubmit,
|
|
8290
|
+
className: "rounded-md bg-indigo-600 px-3 py-2 text-white disabled:cursor-not-allowed disabled:opacity-50"
|
|
8291
|
+
},
|
|
8292
|
+
uploading ? "\u4E0A\u4F20\u4E2D..." : "\u5F00\u59CB\u4E0A\u4F20"
|
|
8293
|
+
));
|
|
8294
|
+
};
|
|
8295
|
+
var BoothRedeemPanel = ({ onRedeem, loading }) => {
|
|
8296
|
+
const [matchCode, setMatchCode] = React69.useState("");
|
|
8297
|
+
const [record, setRecord] = React69.useState(null);
|
|
8298
|
+
const [error, setError] = React69.useState(null);
|
|
8299
|
+
const handleRedeem = async () => {
|
|
8300
|
+
setError(null);
|
|
8301
|
+
const result = await onRedeem(matchCode.trim());
|
|
8302
|
+
if (!result) {
|
|
8303
|
+
setRecord(null);
|
|
8304
|
+
setError("\u5339\u914D\u7801\u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5");
|
|
8305
|
+
return;
|
|
8306
|
+
}
|
|
8307
|
+
if (result.status !== "active") {
|
|
8308
|
+
setRecord(result);
|
|
8309
|
+
setError("\u5339\u914D\u7801\u5DF2\u8FC7\u671F\u6216\u5931\u6548");
|
|
8310
|
+
return;
|
|
8311
|
+
}
|
|
8312
|
+
setRecord(result);
|
|
8313
|
+
};
|
|
8314
|
+
return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React69__namespace.default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u51ED\u5339\u914D\u7801\u4E0B\u8F7D"), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8315
|
+
"input",
|
|
8316
|
+
{
|
|
8317
|
+
value: matchCode,
|
|
8318
|
+
onChange: (e) => setMatchCode(e.target.value.toUpperCase()),
|
|
8319
|
+
placeholder: "\u8F93\u5165\u5339\u914D\u7801\uFF08\u5982 A7K9Q2\uFF09",
|
|
8320
|
+
className: "w-full rounded-md border px-3 py-2 text-sm uppercase"
|
|
8321
|
+
}
|
|
8322
|
+
), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8323
|
+
"button",
|
|
8324
|
+
{
|
|
8325
|
+
type: "button",
|
|
8326
|
+
onClick: handleRedeem,
|
|
8327
|
+
disabled: loading,
|
|
8328
|
+
className: "rounded-md bg-slate-900 px-3 py-2 text-white disabled:opacity-50"
|
|
8329
|
+
},
|
|
8330
|
+
"\u67E5\u8BE2"
|
|
8331
|
+
)), error && /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 rounded-md bg-amber-50 p-2 text-sm text-amber-700" }, error), record && /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "rounded-md border border-slate-100 p-3" }, /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-2 text-xs text-slate-500" }, "\u5171 ", record.files.length, " \u4E2A\u6587\u4EF6"), /* @__PURE__ */ React69__namespace.default.createElement("ul", { className: "space-y-1 text-sm" }, record.files.map((file) => /* @__PURE__ */ React69__namespace.default.createElement("li", { key: file.id, className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement("span", { className: "truncate" }, file.fileName), /* @__PURE__ */ React69__namespace.default.createElement("a", { href: file.objectKey, className: "text-indigo-600 hover:underline", download: true }, "\u4E0B\u8F7D"))))));
|
|
8332
|
+
};
|
|
8333
|
+
var BoothSuccessCard = ({
|
|
8334
|
+
matchCode,
|
|
8335
|
+
expiresAt,
|
|
8336
|
+
downloadUrlPath,
|
|
8337
|
+
onCopyCode,
|
|
8338
|
+
className
|
|
8339
|
+
}) => {
|
|
8340
|
+
const handleCopy = async () => {
|
|
8341
|
+
try {
|
|
8342
|
+
await navigator.clipboard.writeText(matchCode);
|
|
8343
|
+
} catch {
|
|
8344
|
+
}
|
|
8345
|
+
onCopyCode?.(matchCode);
|
|
8346
|
+
};
|
|
8347
|
+
return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: `rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm ${className ?? ""}` }, /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-2 text-xs text-emerald-800" }, "\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5DF2\u751F\u6210\u5339\u914D\u7801"), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 text-2xl font-bold tracking-widest text-emerald-900" }, matchCode), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "mb-3 text-xs text-emerald-800" }, "\u8FC7\u671F\u65F6\u95F4\uFF1A", new Date(expiresAt).toLocaleString()), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8348
|
+
"button",
|
|
8349
|
+
{
|
|
8350
|
+
type: "button",
|
|
8351
|
+
onClick: handleCopy,
|
|
8352
|
+
className: "rounded-md bg-emerald-600 px-3 py-2 text-white hover:bg-emerald-700"
|
|
8353
|
+
},
|
|
8354
|
+
"\u590D\u5236\u5339\u914D\u7801"
|
|
8355
|
+
), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8356
|
+
"a",
|
|
8357
|
+
{
|
|
8358
|
+
href: downloadUrlPath,
|
|
8359
|
+
className: "rounded-md border border-emerald-400 bg-white px-3 py-2 text-emerald-700 hover:bg-emerald-100"
|
|
8360
|
+
},
|
|
8361
|
+
"\u6253\u5F00\u4E0B\u8F7D\u9875"
|
|
8362
|
+
)));
|
|
8363
|
+
};
|
|
8364
|
+
var BoothConfigPage = ({ initialConfig, onSave }) => {
|
|
8365
|
+
const [config, setConfig] = React69.useState(
|
|
8366
|
+
normalizeVocaloidBoothConfig(initialConfig)
|
|
8367
|
+
);
|
|
8368
|
+
const [saving, setSaving] = React69.useState(false);
|
|
8369
|
+
const extText = React69.useMemo(() => config.allowedExtensions.join(","), [config.allowedExtensions]);
|
|
8370
|
+
const update = (key, value) => setConfig((prev) => ({ ...prev, [key]: value }));
|
|
8371
|
+
const save = async () => {
|
|
8372
|
+
setSaving(true);
|
|
8373
|
+
try {
|
|
8374
|
+
const normalized = normalizeVocaloidBoothConfig(config);
|
|
8375
|
+
setConfig(normalized);
|
|
8376
|
+
await onSave?.(normalized);
|
|
8377
|
+
} finally {
|
|
8378
|
+
setSaving(false);
|
|
8379
|
+
}
|
|
8380
|
+
};
|
|
8381
|
+
const reset = () => setConfig(defaultVocaloidBoothConfig);
|
|
8382
|
+
return /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-3" }, /* @__PURE__ */ React69__namespace.default.createElement("h3", { className: "text-lg font-semibold" }, "Vocaloid Booth \u914D\u7F6E\u9875"), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm" }, /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", value: config.boothId, onChange: (e) => update("boothId", e.target.value), placeholder: "boothId" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", value: config.title, onChange: (e) => update("title", e.target.value), placeholder: "\u6807\u9898" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2 md:col-span-2", value: config.description ?? "", onChange: (e) => update("description", e.target.value), placeholder: "\u63CF\u8FF0" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.defaultTtlHours, onChange: (e) => update("defaultTtlHours", Number(e.target.value) || 1), placeholder: "\u9ED8\u8BA4\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxFiles, onChange: (e) => update("maxFiles", Number(e.target.value) || 1), placeholder: "\u6700\u5927\u6587\u4EF6\u6570" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxSingleFileSizeMb, onChange: (e) => update("maxSingleFileSizeMb", Number(e.target.value) || 1), placeholder: "\u5355\u6587\u4EF6\u4E0A\u9650 MB" }), /* @__PURE__ */ React69__namespace.default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxTotalFileSizeMb, onChange: (e) => update("maxTotalFileSizeMb", Number(e.target.value) || 1), placeholder: "\u603B\u5927\u5C0F\u4E0A\u9650 MB" }), /* @__PURE__ */ React69__namespace.default.createElement(
|
|
8383
|
+
"textarea",
|
|
8384
|
+
{
|
|
8385
|
+
className: "rounded border px-3 py-2 md:col-span-2",
|
|
8386
|
+
rows: 3,
|
|
8387
|
+
value: extText,
|
|
8388
|
+
onChange: (e) => update(
|
|
8389
|
+
"allowedExtensions",
|
|
8390
|
+
e.target.value.split(",").map((v) => v.trim()).filter(Boolean)
|
|
8391
|
+
),
|
|
8392
|
+
placeholder: "\u5141\u8BB8\u540E\u7F00\uFF0C\u9017\u53F7\u5206\u9694"
|
|
8393
|
+
}
|
|
8394
|
+
)), /* @__PURE__ */ React69__namespace.default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React69__namespace.default.createElement("button", { className: "rounded bg-indigo-600 px-3 py-2 text-white", disabled: saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"), /* @__PURE__ */ React69__namespace.default.createElement("button", { className: "rounded border px-3 py-2", onClick: reset }, "\u6062\u590D\u9ED8\u8BA4")));
|
|
8395
|
+
};
|
|
8396
|
+
|
|
8041
8397
|
// src/storage/adapters/react-native-adapter.ts
|
|
8042
8398
|
var AsyncStorage = null;
|
|
8043
8399
|
try {
|
|
@@ -8410,6 +8766,11 @@ exports.AvatarImage = AvatarImage;
|
|
|
8410
8766
|
exports.BackButton = BackButton;
|
|
8411
8767
|
exports.BackgroundRemover = BackgroundRemover;
|
|
8412
8768
|
exports.Badge = Badge;
|
|
8769
|
+
exports.BoothConfigPage = BoothConfigPage;
|
|
8770
|
+
exports.BoothRedeemPanel = BoothRedeemPanel;
|
|
8771
|
+
exports.BoothSuccessCard = BoothSuccessCard;
|
|
8772
|
+
exports.BoothUploadPanel = BoothUploadPanel;
|
|
8773
|
+
exports.BoothVaultService = BoothVaultService;
|
|
8413
8774
|
exports.Button = Button;
|
|
8414
8775
|
exports.Card = Card;
|
|
8415
8776
|
exports.CardContent = CardContent;
|
|
@@ -8558,10 +8919,12 @@ exports.createLogger = createLogger;
|
|
|
8558
8919
|
exports.createOpenAICompatibleProvider = createOpenAICompatibleProvider;
|
|
8559
8920
|
exports.createSkillRegistry = createSkillRegistry;
|
|
8560
8921
|
exports.debugUtils = debugUtils;
|
|
8922
|
+
exports.defaultVocaloidBoothConfig = defaultVocaloidBoothConfig;
|
|
8561
8923
|
exports.errorUtils = errorUtils;
|
|
8562
8924
|
exports.fileUtils = fileUtils;
|
|
8563
8925
|
exports.filterExperiments = filterExperiments;
|
|
8564
8926
|
exports.formatTime = formatTime;
|
|
8927
|
+
exports.generateMatchCode = generateMatchCode;
|
|
8565
8928
|
exports.getAllTags = getAllTags;
|
|
8566
8929
|
exports.getCategoryColor = getCategoryColor;
|
|
8567
8930
|
exports.getCategoryDisplayName = getCategoryDisplayName;
|
|
@@ -8572,7 +8935,9 @@ exports.getExperimentCounts = getExperimentCounts;
|
|
|
8572
8935
|
exports.japaneseUtils = japaneseUtils;
|
|
8573
8936
|
exports.logger = logger;
|
|
8574
8937
|
exports.normalizeFestivalCardConfig = normalizeFestivalCardConfig;
|
|
8938
|
+
exports.normalizeMatchCode = normalizeMatchCode;
|
|
8575
8939
|
exports.normalizePromptVariables = normalizePromptVariables;
|
|
8940
|
+
exports.normalizeVocaloidBoothConfig = normalizeVocaloidBoothConfig;
|
|
8576
8941
|
exports.resizeFestivalCardPages = resizeFestivalCardPages;
|
|
8577
8942
|
exports.resolveScreenReceiverSignalUrl = resolveScreenReceiverSignalUrl;
|
|
8578
8943
|
exports.skillToToolDefinition = skillToToolDefinition;
|