routes/repo-write.js - git - Webernet Explorer _X
git 🏠 Home 👤 Sign up Self hosted git, retro shell
★ otgit, code hosting from the year 2000 ★
📄 octuna / routes / repo-write.js
133 lines · 5.5 KB · Raw
1const fs = require('fs');
2const auth = require('../lib/auth');
3const data = require('../lib/data');
4const git = require('../lib/git');
5const render = require('../lib/render');
6const respond = require('../lib/respond');
7const validate = require('../lib/validate');
8const config = require('../lib/config');
9const limit = require('../lib/limit');
10 
11const writeLimiter = limit.make(
12 config.get('limits.write.max'),
13 config.get('limits.write.windowMin') * 60000
14);
15 
16function requireOwner(req, res, owner) {
17 const session = auth.requireAuth(req, res);
18 if (!session) return null;
19 if (session.username !== owner) { respond.forbidden(res); return null; }
20 return session;
21}
22 
23function settingsPage(req, res, params) {
24 const session = auth.requireAuth(req, res);
25 if (!session) return respond.redirect(res, '/signup');
26 if (session.username !== params.owner) return respond.forbidden(res);
27 const repo = data.repos().get(`${params.owner}/${params.repo}`);
28 if (!repo) return respond.notFound(res);
29 const content = render.fill(render.loadView('repo-settings.html'), {
30 OWNER: render.escape(params.owner),
31 REPO: render.escape(params.repo),
32 DESCRIPTION: render.escape(repo.description || ''),
33 VIS_PUBLIC: repo.visibility === 'public' ? 'checked' : '',
34 VIS_PRIVATE: repo.visibility === 'private' ? 'checked' : ''
35 });
36 res.end(render.page({ title: `Settings - ${params.owner}/${params.repo}`, content, session, addressUrl: req.url }));
37}
38 
39function createRepo(req, res) {
40 const session = auth.requireAuth(req, res);
41 if (!session) return;
42 const ip = respond.clientIp(req);
43 if (!writeLimiter(ip)) return respond.tooMany(res);
44 respond.readJson(req, (err, body) => {
45 if (err) return respond.badRequest(res, err.message);
46 const name = String(body.name || '').toLowerCase().trim();
47 const description = String(body.description || '').slice(0, 256);
48 const visibility = body.visibility === 'private' && config.get('repos.allowPrivate')
49 ? 'private' : config.get('repos.defaultVisibility');
50 if (!validate.isValidName(name)) return respond.badRequest(res, 'Invalid repository name');
51 const owner = session.username;
52 const key = `${owner}/${name}`;
53 const repos = data.repos();
54 if (repos.has(key)) return respond.json(res, 409, { error: 'Repository name taken' });
55 const ownerCount = repos.values().filter(r => r.owner === owner).length;
56 if (ownerCount >= config.get('repos.maxPerUser')) {
57 return respond.json(res, 409, { error: 'Repository limit reached' });
58 }
59 const gitDir = git.repoPath(owner, name);
60 git.init(gitDir)
61 .then(() => {
62 repos.set(key, { name, owner, description, visibility, createdAt: Date.now() });
63 respond.json(res, 201, { ok: true, owner, name });
64 })
65 .catch(e => respond.serverError(res, e.message));
66 });
67}
68 
69function updateSettings(req, res, params) {
70 const session = requireOwner(req, res, params.owner);
71 if (!session) return;
72 respond.readJson(req, (err, body) => {
73 if (err) return respond.badRequest(res, err.message);
74 const key = `${params.owner}/${params.repo}`;
75 const repos = data.repos();
76 const repo = repos.get(key);
77 if (!repo) return respond.notFound(res);
78 const updated = { ...repo };
79 if (typeof body.description === 'string') updated.description = body.description.slice(0, 256);
80 if (validate.isVisibility(body.visibility)) {
81 if (body.visibility === 'private' && !config.get('repos.allowPrivate')) {
82 return respond.badRequest(res, 'Private repositories are disabled');
83 }
84 updated.visibility = body.visibility;
85 }
86 repos.set(key, updated);
87 respond.json(res, 200, { ok: true });
88 });
89}
90 
91function renameRepo(req, res, params) {
92 const session = requireOwner(req, res, params.owner);
93 if (!session) return;
94 respond.readJson(req, (err, body) => {
95 if (err) return respond.badRequest(res, err.message);
96 const newName = String(body.name || '').toLowerCase().trim();
97 if (!validate.isValidName(newName)) return respond.badRequest(res, 'Invalid name');
98 const oldKey = `${params.owner}/${params.repo}`;
99 const newKey = `${params.owner}/${newName}`;
100 const repos = data.repos();
101 const repo = repos.get(oldKey);
102 if (!repo) return respond.notFound(res);
103 if (repos.has(newKey)) return respond.json(res, 409, { error: 'Name already taken' });
104 try {
105 fs.renameSync(git.repoPath(params.owner, params.repo), git.repoPath(params.owner, newName));
106 } catch (e) { return respond.serverError(res, 'Could not rename directory'); }
107 repos.delete(oldKey);
108 repos.set(newKey, { ...repo, name: newName });
109 respond.json(res, 200, { ok: true, owner: params.owner, name: newName });
110 });
111}
112 
113function deleteRepo(req, res, params) {
114 const session = requireOwner(req, res, params.owner);
115 if (!session) return;
116 const key = `${params.owner}/${params.repo}`;
117 const repos = data.repos();
118 if (!repos.has(key)) return respond.notFound(res);
119 const gitDir = git.repoPath(params.owner, params.repo);
120 try { if (fs.existsSync(gitDir)) fs.rmSync(gitDir, { recursive: true, force: true }); }
121 catch (e) { return respond.serverError(res, 'Could not delete repository'); }
122 repos.delete(key);
123 respond.json(res, 200, { ok: true });
124}
125 
126module.exports = function register(router) {
127 router.get('/:owner/:repo/settings', settingsPage);
128 router.post('/api/repo/create', createRepo);
129 router.post('/api/repo/:owner/:repo/settings', updateSettings);
130 router.post('/api/repo/:owner/:repo/rename', renameRepo);
131 router.post('/api/repo/:owner/:repo/delete', deleteRepo);
132};
133 
Done 🔒 Internet