[add] init
This commit is contained in:
parent
d0b1bfdfcd
commit
c333050f3c
268
index.html
Normal file
268
index.html
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-TW">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>手動輸入組織,批次建立 Gitea 儲存庫與團隊</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
background: #f0f0f0;
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
label,
|
||||||
|
input {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h2>
|
||||||
|
輸入組織名稱並從 JSON 建立儲存庫+設定團隊
|
||||||
|
<br />
|
||||||
|
PS.請先建好團隊(或使用下方按鈕批次建立)
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<label for="orgInput">組織名稱:</label>
|
||||||
|
<input type="text" id="orgInput" placeholder="請輸入組織名稱" />
|
||||||
|
<button onclick="loadAndCreateTeams()">創建團隊</button>
|
||||||
|
<button onclick="loadAndCreateRepos()">開始建立儲存庫</button>
|
||||||
|
|
||||||
|
<div id="log"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const accessToken = "587f612b5cdbf59bfa6ebb8e082109a6aa26d435";
|
||||||
|
// https://dash.cloudflare.com/361c3fb1feb10c54c262c3872c51a3a5/workers/services/view/cors-anywhere/production/metrics
|
||||||
|
const proxy = "https://cors-anywhere.bir840124.workers.dev/?url=";
|
||||||
|
const giteaAPIBase = "https://git.catan.com.tw/api/v1";
|
||||||
|
|
||||||
|
const logBox = document.getElementById("log");
|
||||||
|
function log(message) {
|
||||||
|
logBox.textContent += message + "\n";
|
||||||
|
logBox.scrollTop = logBox.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 建立儲存庫功能(維持不變)
|
||||||
|
async function loadAndCreateRepos() {
|
||||||
|
logBox.textContent = ""; // 清空 log
|
||||||
|
const orgInput = document.getElementById("orgInput").value.trim();
|
||||||
|
if (!orgInput) {
|
||||||
|
alert("請輸入組織名稱");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log(`✅ 開始建立儲存庫:${orgInput}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch("repos.json");
|
||||||
|
const repoList = await res.json();
|
||||||
|
|
||||||
|
for (const repo of repoList) {
|
||||||
|
repo.org = orgInput;
|
||||||
|
await createRepoWithSettings(repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
log("✅ 所有儲存庫處理完成!");
|
||||||
|
} catch (err) {
|
||||||
|
log("❌ 讀取 repos.json 失敗:" + err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function repoExists(org, repoName) {
|
||||||
|
const url = `${giteaAPIBase}/orgs/${org}/repos`;
|
||||||
|
const res = await fetch(proxy + url, {
|
||||||
|
headers: { "Authorization": `token ${accessToken}` }
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`查詢儲存庫失敗,狀態碼:${res.status}`);
|
||||||
|
}
|
||||||
|
const repos = await res.json();
|
||||||
|
return repos.some(r => r.name === repoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRepoWithSettings(repo) {
|
||||||
|
if (await repoExists(repo.org, repo.name)) {
|
||||||
|
log(`⚠️ 儲存庫已存在,略過建立:${repo.org}/${repo.name}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createUrl = `${giteaAPIBase}/orgs/${repo.org}/repos`;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name: repo.name,
|
||||||
|
description: repo.description || "",
|
||||||
|
default_branch: repo.default_branch || "master",
|
||||||
|
private: false,
|
||||||
|
auto_init: true
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(proxy + createUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `token ${accessToken}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
log(`✅ 建立儲存庫:${repo.org}/${repo.name}`);
|
||||||
|
|
||||||
|
if (repo.teams?.length > 0) {
|
||||||
|
for (const team of repo.teams) {
|
||||||
|
await addTeamToRepo(repo.org, repo.name, team);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log(`⚠️ 建立失敗:${repo.org}/${repo.name} - ${data.message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
log(`❌ 建立錯誤:${repo.org}/${repo.name} - ${err.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addTeamToRepo(org, repo, teamName) {
|
||||||
|
const teamsURL = `${giteaAPIBase}/orgs/${org}/teams`;
|
||||||
|
const teamRes = await fetch(proxy + teamsURL, {
|
||||||
|
headers: { "Authorization": `token ${accessToken}` }
|
||||||
|
});
|
||||||
|
const teams = await teamRes.json();
|
||||||
|
const targetTeam = teams.find(t => t.name === teamName);
|
||||||
|
|
||||||
|
if (!targetTeam) {
|
||||||
|
log(`⚠️ 找不到團隊:${teamName}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${giteaAPIBase}/teams/${targetTeam.id}/repos/${org}/${repo}`;
|
||||||
|
const res = await fetch(proxy + url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Authorization": `token ${accessToken}` }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
log(`👥 加入團隊:${teamName}`);
|
||||||
|
} else {
|
||||||
|
log(`⚠️ 加團隊失敗:${teamName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增:創建團隊功能
|
||||||
|
async function loadAndCreateTeams() {
|
||||||
|
logBox.textContent = ""; // 清空 log
|
||||||
|
const orgInput = document.getElementById("orgInput").value.trim();
|
||||||
|
if (!orgInput) {
|
||||||
|
alert("請輸入組織名稱");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log(`✅ 開始建立團隊:${orgInput}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 載入 teams.json
|
||||||
|
const res = await fetch("teams.json");
|
||||||
|
const teamList = await res.json();
|
||||||
|
|
||||||
|
for (const team of teamList) {
|
||||||
|
await createTeam(orgInput, team);
|
||||||
|
}
|
||||||
|
|
||||||
|
log("✅ 所有團隊建立完成!");
|
||||||
|
} catch (err) {
|
||||||
|
log("❌ 讀取 teams.json 失敗:" + err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTeam(org, team) {
|
||||||
|
const url = `${giteaAPIBase}/orgs/${org}/teams`;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
name: team.name,
|
||||||
|
description: team.description || "",
|
||||||
|
permission: "none",
|
||||||
|
units: team.units || [],
|
||||||
|
units_map: team.units_map || {},
|
||||||
|
can_create_org_repo: false,
|
||||||
|
includes_all_repositories: false
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(proxy + url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `token ${accessToken}`
|
||||||
|
},
|
||||||
|
body: JSON.stringify(payload)
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
log(`✅ 團隊建立成功:${team.name}`);
|
||||||
|
if (team.members?.length > 0) {
|
||||||
|
for (const username of team.members) {
|
||||||
|
await addMemberToTeam(data.id, username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (data.message?.includes("team already exists")) {
|
||||||
|
log(`⚠️ 團隊已存在,略過:${team.name}`);
|
||||||
|
} else {
|
||||||
|
log(`⚠️ 建立團隊失敗:${team.name} - ${data.message}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log(`❌ 團隊建立錯誤:${team.name} - ${err.message}`);
|
||||||
|
}
|
||||||
|
// await delay(1000); // 每次請求間隔 1 秒,避免 rate limit
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addMemberToTeam(teamId, username) {
|
||||||
|
const url = `${giteaAPIBase}/teams/${teamId}/members/${username}`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(proxy + url, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { "Authorization": `token ${accessToken}` }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.ok) {
|
||||||
|
log(`👤 新增成員:${username}`);
|
||||||
|
} else {
|
||||||
|
const data = await res.json();
|
||||||
|
log(`⚠️ 新增成員失敗:${username} - ${data.message || res.statusText}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
log(`❌ 新增成員錯誤:${username} - ${err.message}`);
|
||||||
|
}
|
||||||
|
// await delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delay(ms) {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
110
repos.json
Normal file
110
repos.json
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "Client-Core",
|
||||||
|
"description": "前端平台大廳(前端美術寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "WebUI",
|
||||||
|
"description": "",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Official-WebDesign",
|
||||||
|
"description": "官網網頁設計(美術.前端寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Client-Core-WebDesign",
|
||||||
|
"description": "平台網頁設計(美術f前端寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Html-AddToHomeScreen",
|
||||||
|
"description": "手刻添加捷徑到桌面HTML說明頁(前端.美術寫入)(前端手動搬去Resource)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Html-PurchaseResultPage",
|
||||||
|
"description": "手刻第三方購買結果頁面(前端.美術寫入)(前端手動搬去Resource)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Resource-Internal",
|
||||||
|
"description": "外載資源-內版開發(前端美術企劃寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtWrite",
|
||||||
|
"FrontendWrite",
|
||||||
|
"PlanWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Resource-B2B",
|
||||||
|
"description": "外載資源-廠商串接(前端寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Resource-Out",
|
||||||
|
"description": "外載資源-外版(前端寫入)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"FrontendWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Plan-Form",
|
||||||
|
"description": "Excel設定表(企劃前端寫入.後端讀取)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"BackendRead",
|
||||||
|
"FrontendRead",
|
||||||
|
"PlanWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Plan-Form_Json",
|
||||||
|
"description": "Plan-Form子模組-Resource-Internal引用(企劃寫入.前端讀取)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"FrontendRead",
|
||||||
|
"PlanWrite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Plan-Form_Table",
|
||||||
|
"description": "Plan-Form子模組-Client-Core引用(企劃前端寫入.美術讀取)",
|
||||||
|
"default_branch": "master",
|
||||||
|
"teams": [
|
||||||
|
"ArtRead",
|
||||||
|
"FrontendWrite",
|
||||||
|
"PlanWrite"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
116
teams.json
Normal file
116
teams.json
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "ArtRead",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "read",
|
||||||
|
"repo.issues": "read",
|
||||||
|
"repo.pulls": "read",
|
||||||
|
"repo.releases": "read",
|
||||||
|
"repo.wiki": "read",
|
||||||
|
"repo.projects": "read",
|
||||||
|
"repo.packages": "read",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ArtWrite",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "write",
|
||||||
|
"repo.issues": "write",
|
||||||
|
"repo.pulls": "write",
|
||||||
|
"repo.releases": "write",
|
||||||
|
"repo.wiki": "write",
|
||||||
|
"repo.projects": "write",
|
||||||
|
"repo.packages": "read",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "BackendRead",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "read",
|
||||||
|
"repo.issues": "read",
|
||||||
|
"repo.pulls": "read",
|
||||||
|
"repo.releases": "read",
|
||||||
|
"repo.wiki": "read",
|
||||||
|
"repo.projects": "read",
|
||||||
|
"repo.packages": "read",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
"jerky",
|
||||||
|
"tommylin",
|
||||||
|
"wnoirck",
|
||||||
|
"windydo",
|
||||||
|
"vint0716"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FrontendRead",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "read",
|
||||||
|
"repo.issues": "read",
|
||||||
|
"repo.pulls": "read",
|
||||||
|
"repo.releases": "read",
|
||||||
|
"repo.wiki": "read",
|
||||||
|
"repo.projects": "read",
|
||||||
|
"repo.packages": "read",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
"publicfrontend",
|
||||||
|
"Awan",
|
||||||
|
"derek100232",
|
||||||
|
"cathy19804"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FrontendWrite",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "write",
|
||||||
|
"repo.issues": "write",
|
||||||
|
"repo.pulls": "write",
|
||||||
|
"repo.releases": "write",
|
||||||
|
"repo.wiki": "write",
|
||||||
|
"repo.projects": "write",
|
||||||
|
"repo.packages": "read",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
"publicfrontend",
|
||||||
|
"Awan",
|
||||||
|
"derek100232",
|
||||||
|
"cathy19804"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "PlanWrite",
|
||||||
|
"units_map": {
|
||||||
|
"repo.code": "write",
|
||||||
|
"repo.issues": "read",
|
||||||
|
"repo.pulls": "write",
|
||||||
|
"repo.releases": "write",
|
||||||
|
"repo.wiki": "write",
|
||||||
|
"repo.projects": "write",
|
||||||
|
"repo.packages": "none",
|
||||||
|
"repo.actions": "read",
|
||||||
|
"repo.ext_wiki": "read",
|
||||||
|
"repo.ext_issues": "read"
|
||||||
|
},
|
||||||
|
"members": [
|
||||||
|
"Brandy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
Loading…
x
Reference in New Issue
Block a user