chore(frontend): introduce prettier + eslint and prune unused code
- Add Prettier 3 with prettier-plugin-organize-imports (sorts/removes unused imports) - Add ESLint 10 flat config with typescript-eslint + eslint-plugin-vue + eslint-config-prettier - Add root scripts: format, format:check, lint, lint:fix - Reformat 257 files across admin-web, ops-web, desktop-client, packages - Remove unused locals/exports flagged by --noUnusedLocals/--noUnusedParameters - Fix duplicate localTabLabel key, surrogate-pair regex u-flag, NBSP literal in regex - Skip server/ (Go) and apps/browser-extension/ (deprecated per ADR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,101 +1,101 @@
|
||||
type ToutiaoMediaInfoResponse = {
|
||||
data?: {
|
||||
user?: {
|
||||
id_str?: string;
|
||||
screen_name?: string;
|
||||
https_avatar_url?: string;
|
||||
};
|
||||
id_str?: string
|
||||
screen_name?: string
|
||||
https_avatar_url?: string
|
||||
}
|
||||
media?: {
|
||||
has_third_party_ad_permission?: boolean;
|
||||
has_toutiao_ad_permission?: boolean;
|
||||
};
|
||||
};
|
||||
code?: number;
|
||||
message?: string;
|
||||
};
|
||||
has_third_party_ad_permission?: boolean
|
||||
has_toutiao_ad_permission?: boolean
|
||||
}
|
||||
}
|
||||
code?: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
type ToutiaoUploadPictureResponse = {
|
||||
url?: string;
|
||||
web_uri?: string;
|
||||
origin_web_uri?: string;
|
||||
rigin_web_uri?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
url?: string
|
||||
web_uri?: string
|
||||
origin_web_uri?: string
|
||||
rigin_web_uri?: string
|
||||
width?: number
|
||||
height?: number
|
||||
}
|
||||
|
||||
type ToutiaoSpiceImageResponse = {
|
||||
data?: {
|
||||
image_url?: string;
|
||||
image_uri?: string;
|
||||
image_width?: number;
|
||||
image_height?: number;
|
||||
};
|
||||
};
|
||||
image_url?: string
|
||||
image_uri?: string
|
||||
image_width?: number
|
||||
image_height?: number
|
||||
}
|
||||
}
|
||||
|
||||
type ToutiaoPublishResponse = {
|
||||
code?: number;
|
||||
message?: string;
|
||||
code?: number
|
||||
message?: string
|
||||
data?: {
|
||||
pgc_id?: string | number;
|
||||
};
|
||||
};
|
||||
pgc_id?: string | number
|
||||
}
|
||||
}
|
||||
|
||||
export interface ToutiaoMediaSnapshot {
|
||||
platformUid: string | null;
|
||||
screenName: string | null;
|
||||
avatarUrl: string | null;
|
||||
hasAdPermission: boolean;
|
||||
platformUid: string | null
|
||||
screenName: string | null
|
||||
avatarUrl: string | null
|
||||
hasAdPermission: boolean
|
||||
}
|
||||
|
||||
export interface ToutiaoPublishArticleInput {
|
||||
title: string;
|
||||
html: string;
|
||||
coverAssetUrl?: string | null;
|
||||
publishType: "publish" | "draft";
|
||||
title: string
|
||||
html: string
|
||||
coverAssetUrl?: string | null
|
||||
publishType: 'publish' | 'draft'
|
||||
}
|
||||
|
||||
export interface ToutiaoPublishTransport {
|
||||
fetchJson<T>(input: string, init?: RequestInit): Promise<T>;
|
||||
fetchImageBlob(sourceUrl: string): Promise<Blob | null>;
|
||||
fetchJson<T>(input: string, init?: RequestInit): Promise<T>
|
||||
fetchImageBlob(sourceUrl: string): Promise<Blob | null>
|
||||
uploadHtmlImages(
|
||||
html: string,
|
||||
uploader: (sourceUrl: string) => Promise<string | null>,
|
||||
): Promise<{ html: string }>;
|
||||
reportProgress?(stage: "media_info" | "upload_content_images" | "upload_cover" | "submit"): void;
|
||||
): Promise<{ html: string }>
|
||||
reportProgress?(stage: 'media_info' | 'upload_content_images' | 'upload_cover' | 'submit'): void
|
||||
}
|
||||
|
||||
export type ToutiaoPublishResult =
|
||||
| {
|
||||
success: true;
|
||||
status: "success" | "pending_review";
|
||||
articleId: string;
|
||||
mediaName: string;
|
||||
externalManageUrl: string;
|
||||
externalArticleUrl: string | null;
|
||||
message: string;
|
||||
success: true
|
||||
status: 'success' | 'pending_review'
|
||||
articleId: string
|
||||
mediaName: string
|
||||
externalManageUrl: string
|
||||
externalArticleUrl: string | null
|
||||
message: string
|
||||
}
|
||||
| {
|
||||
success: false;
|
||||
status: "failed";
|
||||
code: "toutiaohao_not_logged_in" | "article_content_empty" | "toutiaohao_publish_failed";
|
||||
message: string;
|
||||
};
|
||||
success: false
|
||||
status: 'failed'
|
||||
code: 'toutiaohao_not_logged_in' | 'article_content_empty' | 'toutiaohao_publish_failed'
|
||||
message: string
|
||||
}
|
||||
|
||||
export async function fetchToutiaoMediaSnapshot(
|
||||
fetchJson: <T>(input: string, init?: RequestInit) => Promise<T>,
|
||||
): Promise<ToutiaoMediaSnapshot | null> {
|
||||
const response = await fetchJson<ToutiaoMediaInfoResponse>(
|
||||
"https://mp.toutiao.com/mp/agw/media/get_media_info",
|
||||
'https://mp.toutiao.com/mp/agw/media/get_media_info',
|
||||
{
|
||||
headers: {
|
||||
accept: "application/json, text/plain, */*",
|
||||
accept: 'application/json, text/plain, */*',
|
||||
},
|
||||
},
|
||||
).catch(() => null);
|
||||
).catch(() => null)
|
||||
|
||||
const user = response?.data?.user;
|
||||
const user = response?.data?.user
|
||||
if (!user?.id_str || !user.screen_name) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -103,218 +103,227 @@ export async function fetchToutiaoMediaSnapshot(
|
||||
screenName: user.screen_name,
|
||||
avatarUrl: user.https_avatar_url ?? null,
|
||||
hasAdPermission: Boolean(
|
||||
response?.data?.media?.has_third_party_ad_permission
|
||||
|| response?.data?.media?.has_toutiao_ad_permission,
|
||||
response?.data?.media?.has_third_party_ad_permission ||
|
||||
response?.data?.media?.has_toutiao_ad_permission,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadCover(
|
||||
transport: ToutiaoPublishTransport,
|
||||
sourceUrl: string,
|
||||
publishType: "publish" | "draft",
|
||||
publishType: 'publish' | 'draft',
|
||||
) {
|
||||
const blob = await transport.fetchImageBlob(sourceUrl);
|
||||
const blob = await transport.fetchImageBlob(sourceUrl)
|
||||
if (!blob) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
if (publishType === "publish") {
|
||||
const form = new FormData();
|
||||
form.append("upfile", blob, "cover.png");
|
||||
const uploaded = await transport.fetchJson<ToutiaoUploadPictureResponse>(
|
||||
"https://mp.toutiao.com/mp/agw/article_material/photo/upload_picture",
|
||||
{
|
||||
method: "POST",
|
||||
body: form,
|
||||
},
|
||||
).catch(() => null);
|
||||
if (publishType === 'publish') {
|
||||
const form = new FormData()
|
||||
form.append('upfile', blob, 'cover.png')
|
||||
const uploaded = await transport
|
||||
.fetchJson<ToutiaoUploadPictureResponse>(
|
||||
'https://mp.toutiao.com/mp/agw/article_material/photo/upload_picture',
|
||||
{
|
||||
method: 'POST',
|
||||
body: form,
|
||||
},
|
||||
)
|
||||
.catch(() => null)
|
||||
|
||||
if (!uploaded?.url || !uploaded.web_uri) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: 0,
|
||||
url: uploaded.url,
|
||||
uri: uploaded.web_uri,
|
||||
origin_uri: uploaded.origin_web_uri ?? uploaded.rigin_web_uri ?? "",
|
||||
origin_uri: uploaded.origin_web_uri ?? uploaded.rigin_web_uri ?? '',
|
||||
thumb_width: uploaded.width ?? 0,
|
||||
thumb_height: uploaded.height ?? 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const firstForm = new FormData();
|
||||
firstForm.append("image", blob, "cover.png");
|
||||
const first = await transport.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
"https://mp.toutiao.com/spice/image?device_platform=web",
|
||||
{
|
||||
method: "POST",
|
||||
body: firstForm,
|
||||
},
|
||||
).catch(() => null);
|
||||
const firstForm = new FormData()
|
||||
firstForm.append('image', blob, 'cover.png')
|
||||
const first = await transport
|
||||
.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
'https://mp.toutiao.com/spice/image?device_platform=web',
|
||||
{
|
||||
method: 'POST',
|
||||
body: firstForm,
|
||||
},
|
||||
)
|
||||
.catch(() => null)
|
||||
|
||||
const imageUrl = first?.data?.image_url;
|
||||
const imageUrl = first?.data?.image_url
|
||||
if (!imageUrl) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
const secondForm = new FormData();
|
||||
secondForm.append("imageUrl", imageUrl);
|
||||
const final = await transport.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
"https://mp.toutiao.com/spice/image?device_platform=web&need_cover_url=1",
|
||||
{
|
||||
method: "POST",
|
||||
body: secondForm,
|
||||
},
|
||||
).catch(() => null);
|
||||
const secondForm = new FormData()
|
||||
secondForm.append('imageUrl', imageUrl)
|
||||
const final = await transport
|
||||
.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
'https://mp.toutiao.com/spice/image?device_platform=web&need_cover_url=1',
|
||||
{
|
||||
method: 'POST',
|
||||
body: secondForm,
|
||||
},
|
||||
)
|
||||
.catch(() => null)
|
||||
|
||||
if (!final?.data?.image_url || !first.data?.image_uri) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
id: "",
|
||||
id: '',
|
||||
url: final.data.image_url,
|
||||
uri: first.data.image_uri,
|
||||
ic_uri: "",
|
||||
ic_uri: '',
|
||||
thumb_width: first.data.image_width ?? 0,
|
||||
thumb_height: first.data.image_height ?? 0,
|
||||
extra: {
|
||||
from_content_uri: "",
|
||||
from_content: "0",
|
||||
from_content_uri: '',
|
||||
from_content: '0',
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadContentImage(
|
||||
transport: ToutiaoPublishTransport,
|
||||
sourceUrl: string,
|
||||
): Promise<string | null> {
|
||||
const blob = await transport.fetchImageBlob(sourceUrl);
|
||||
const blob = await transport.fetchImageBlob(sourceUrl)
|
||||
if (!blob) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
const form = new FormData();
|
||||
form.append("image", blob, "image.png");
|
||||
const uploaded = await transport.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
"https://mp.toutiao.com/spice/image?device_platform=web",
|
||||
{
|
||||
method: "POST",
|
||||
body: form,
|
||||
},
|
||||
).catch(() => null);
|
||||
const form = new FormData()
|
||||
form.append('image', blob, 'image.png')
|
||||
const uploaded = await transport
|
||||
.fetchJson<ToutiaoSpiceImageResponse>(
|
||||
'https://mp.toutiao.com/spice/image?device_platform=web',
|
||||
{
|
||||
method: 'POST',
|
||||
body: form,
|
||||
},
|
||||
)
|
||||
.catch(() => null)
|
||||
|
||||
return uploaded?.data?.image_url ?? null;
|
||||
return uploaded?.data?.image_url ?? null
|
||||
}
|
||||
|
||||
export async function publishToutiaoArticle(
|
||||
input: ToutiaoPublishArticleInput,
|
||||
transport: ToutiaoPublishTransport,
|
||||
): Promise<ToutiaoPublishResult> {
|
||||
transport.reportProgress?.("media_info");
|
||||
const media = await fetchToutiaoMediaSnapshot(transport.fetchJson);
|
||||
transport.reportProgress?.('media_info')
|
||||
const media = await fetchToutiaoMediaSnapshot(transport.fetchJson)
|
||||
if (!media?.platformUid || !media.screenName) {
|
||||
return {
|
||||
success: false,
|
||||
status: "failed",
|
||||
code: "toutiaohao_not_logged_in",
|
||||
message: "未检测到头条号登录态",
|
||||
};
|
||||
status: 'failed',
|
||||
code: 'toutiaohao_not_logged_in',
|
||||
message: '未检测到头条号登录态',
|
||||
}
|
||||
}
|
||||
|
||||
const normalizedHtml = input.html.trim();
|
||||
const normalizedHtml = input.html.trim()
|
||||
if (!normalizedHtml) {
|
||||
return {
|
||||
success: false,
|
||||
status: "failed",
|
||||
code: "article_content_empty",
|
||||
message: "html_content is empty",
|
||||
};
|
||||
status: 'failed',
|
||||
code: 'article_content_empty',
|
||||
message: 'html_content is empty',
|
||||
}
|
||||
}
|
||||
|
||||
transport.reportProgress?.("upload_content_images");
|
||||
const processed = await transport.uploadHtmlImages(
|
||||
normalizedHtml,
|
||||
async (sourceUrl) => uploadContentImage(transport, sourceUrl),
|
||||
);
|
||||
transport.reportProgress?.('upload_content_images')
|
||||
const processed = await transport.uploadHtmlImages(normalizedHtml, async (sourceUrl) =>
|
||||
uploadContentImage(transport, sourceUrl),
|
||||
)
|
||||
|
||||
transport.reportProgress?.("upload_cover");
|
||||
transport.reportProgress?.('upload_cover')
|
||||
const cover = input.coverAssetUrl?.trim()
|
||||
? await uploadCover(transport, input.coverAssetUrl.trim(), input.publishType)
|
||||
: null;
|
||||
: null
|
||||
|
||||
const body = new URLSearchParams({
|
||||
content: processed.html,
|
||||
title: input.title,
|
||||
mp_editor_stat: JSON.stringify({ code_block: 1 }),
|
||||
is_refute_rumor: "0",
|
||||
save: input.publishType === "publish" ? "1" : "0",
|
||||
timer_status: "0",
|
||||
is_refute_rumor: '0',
|
||||
save: input.publishType === 'publish' ? '1' : '0',
|
||||
timer_status: '0',
|
||||
draft_form_data: JSON.stringify({ coverType: 1 }),
|
||||
article_ad_type: media.hasAdPermission ? "3" : "2",
|
||||
article_ad_type: media.hasAdPermission ? '3' : '2',
|
||||
pgc_feed_covers: JSON.stringify(cover ? [cover] : []),
|
||||
is_fans_article: "0",
|
||||
govern_forward: "0",
|
||||
praise: "0",
|
||||
disable_praise: "0",
|
||||
tree_plan_article: "0",
|
||||
activity_tag: "0",
|
||||
trends_writing_tag: "0",
|
||||
claim_exclusive: "0",
|
||||
is_fans_article: '0',
|
||||
govern_forward: '0',
|
||||
praise: '0',
|
||||
disable_praise: '0',
|
||||
tree_plan_article: '0',
|
||||
activity_tag: '0',
|
||||
trends_writing_tag: '0',
|
||||
claim_exclusive: '0',
|
||||
info_source: JSON.stringify({
|
||||
source_type: 5,
|
||||
source_author_uid: "",
|
||||
time_format: "",
|
||||
source_author_uid: '',
|
||||
time_format: '',
|
||||
position: {},
|
||||
}),
|
||||
});
|
||||
})
|
||||
|
||||
transport.reportProgress?.("submit");
|
||||
const response: ToutiaoPublishResponse = await transport.fetchJson<ToutiaoPublishResponse>(
|
||||
"https://mp.toutiao.com/mp/agw/article/publish?source=mp&type=article&aid=1231",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
transport.reportProgress?.('submit')
|
||||
const response: ToutiaoPublishResponse = await transport
|
||||
.fetchJson<ToutiaoPublishResponse>(
|
||||
'https://mp.toutiao.com/mp/agw/article/publish?source=mp&type=article&aid=1231',
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body,
|
||||
},
|
||||
body,
|
||||
},
|
||||
).catch((error) => ({
|
||||
code: -1,
|
||||
message: error instanceof Error ? error.message : "toutiaohao_publish_failed",
|
||||
}));
|
||||
)
|
||||
.catch((error) => ({
|
||||
code: -1,
|
||||
message: error instanceof Error ? error.message : 'toutiaohao_publish_failed',
|
||||
}))
|
||||
|
||||
const articleId = response.data?.pgc_id != null ? String(response.data.pgc_id) : "";
|
||||
const articleId = response.data?.pgc_id != null ? String(response.data.pgc_id) : ''
|
||||
if (response.code !== 0 || !articleId) {
|
||||
return {
|
||||
success: false,
|
||||
status: "failed",
|
||||
code: "toutiaohao_publish_failed",
|
||||
message: response.message || "toutiaohao_publish_failed",
|
||||
};
|
||||
status: 'failed',
|
||||
code: 'toutiaohao_publish_failed',
|
||||
message: response.message || 'toutiaohao_publish_failed',
|
||||
}
|
||||
}
|
||||
|
||||
if (input.publishType === "draft") {
|
||||
if (input.publishType === 'draft') {
|
||||
return {
|
||||
success: true,
|
||||
status: "pending_review",
|
||||
status: 'pending_review',
|
||||
articleId,
|
||||
mediaName: media.screenName,
|
||||
externalManageUrl: "https://mp.toutiao.com/profile_v4/graphic/publish",
|
||||
externalManageUrl: 'https://mp.toutiao.com/profile_v4/graphic/publish',
|
||||
externalArticleUrl: null,
|
||||
message: "头条号草稿保存成功。",
|
||||
};
|
||||
message: '头条号草稿保存成功。',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
status: "success",
|
||||
status: 'success',
|
||||
articleId,
|
||||
mediaName: media.screenName,
|
||||
externalManageUrl: "https://mp.toutiao.com/profile_v4/index",
|
||||
externalManageUrl: 'https://mp.toutiao.com/profile_v4/index',
|
||||
externalArticleUrl: `https://www.toutiao.com/article/${articleId}/`,
|
||||
message: "头条号发布成功。",
|
||||
};
|
||||
message: '头条号发布成功。',
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user