[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