| @@ -74,6 +74,7 @@ vendor/bundle/ | |||
| /log | |||
| /public/admin | |||
| /mysql_data | |||
| /public/repo/ | |||
| .generators | |||
| @@ -85,3 +86,4 @@ redis_data/ | |||
| Dockerfile | |||
| dump.rdb | |||
| .tags* | |||
| ceshi_user.xlsx | |||
| @@ -1,529 +0,0 @@ | |||
| import React, {Component} from 'react'; | |||
| import logo from './logo.svg'; | |||
| import './App.css'; | |||
| import {LocaleProvider} from 'antd' | |||
| import zhCN from 'antd/lib/locale-provider/zh_CN'; | |||
| import { | |||
| BrowserRouter as Router, | |||
| Route, | |||
| Switch | |||
| } from 'react-router-dom'; | |||
| import axios from 'axios'; | |||
| import '@icedesign/base/dist/ICEDesignBase.css'; | |||
| import '@icedesign/base/index.scss'; | |||
| import LoginDialog from './modules/login/LoginDialog'; | |||
| import Notcompletedysl from './modules/user/Notcompletedysl'; | |||
| import Trialapplicationysl from './modules/login/Trialapplicationysl'; | |||
| import Trialapplicationreview from './modules/user/Trialapplicationreview'; | |||
| import Addcourses from "./modules/courses/coursesPublic/Addcourses"; | |||
| import AccountProfile from"./modules/user/AccountProfile"; | |||
| import Trialapplication from './modules/login/Trialapplication' | |||
| import NotFoundPage from './NotFoundPage' | |||
| import Loading from './Loading' | |||
| import Loadable from 'react-loadable'; | |||
| import moment from 'moment' | |||
| import {MuiThemeProvider, createMuiTheme} from 'material-ui/styles'; | |||
| // import './AppConfig' | |||
| import history from './history'; | |||
| import {SnackbarHOC} from 'educoder' | |||
| import {initAxiosInterceptors} from './AppConfig' | |||
| // !!!tpi需要这个来加载css | |||
| import {TPMIndexHOC} from './modules/tpm/TPMIndexHOC'; | |||
| const theme = createMuiTheme({ | |||
| palette: { | |||
| primary: { | |||
| main: '#4CACFF', | |||
| contrastText: 'rgba(255, 255, 255, 0.87)' | |||
| }, | |||
| secondary: {main: '#4CACFF'}, // #11cb5f This is just green.A700 as hex. | |||
| }, | |||
| }); | |||
| // | |||
| // const Trialapplication= Loadable({ | |||
| // loader: () =>import('./modules/login/Trialapplication'), | |||
| // loading:Loading, | |||
| // }) | |||
| //登入 | |||
| const EducoderLogin = Loadable({ | |||
| loader: () => import('./modules/login/EducoderLogin'), | |||
| loading: Loading, | |||
| }) | |||
| const TestIndex = Loadable({ | |||
| loader: () => import('./modules/test'), | |||
| loading: Loading, | |||
| }) | |||
| const IndexWrapperComponent = Loadable({ | |||
| loader: () => import('./modules/page/IndexWrapper'), | |||
| loading: Loading, | |||
| }) | |||
| const CommentComponent = Loadable({ | |||
| loader: () => import('./modules/comment/CommentContainer'), | |||
| loading: Loading, | |||
| }) | |||
| const TestMaterialDesignComponent = Loadable({ | |||
| loader: () => import('./modules/test/md/TestMaterialDesign'), | |||
| loading: Loading, | |||
| }) | |||
| const TestCodeMirrorComponent = Loadable({ | |||
| loader: () => import('./modules/test/codemirror/TestCodeMirror'), | |||
| loading: Loading, | |||
| }) | |||
| const TestComponent = Loadable({ | |||
| loader: () => import('./modules/test/TestRC'), | |||
| loading: Loading, | |||
| }) | |||
| const TestUrlQueryComponent = Loadable({ | |||
| loader: () => import('./modules/test/urlquery/TestUrlQuery'), | |||
| loading: Loading, | |||
| }) | |||
| const TPMIndexComponent = Loadable({ | |||
| loader: () => import('./modules/tpm/TPMIndex'), | |||
| loading: Loading, | |||
| }) | |||
| const TPMShixunsIndexComponent = Loadable({ | |||
| loader: () => import('./modules/tpm/shixuns/ShixunsIndex'), | |||
| loading: Loading, | |||
| }) | |||
| //实训课程(原实训路径) | |||
| const ShixunPaths = Loadable({ | |||
| loader: () => import('./modules/paths/Index'), | |||
| loading: Loading, | |||
| }) | |||
| //在线课堂 | |||
| const CoursesIndex = Loadable({ | |||
| loader: () => import('./modules/courses/Index'), | |||
| loading: Loading, | |||
| }) | |||
| const SearchPage = Loadable({ | |||
| loader: () => import('./search/SearchPage'), | |||
| loading: Loading, | |||
| }) | |||
| //教学案例 | |||
| const MoopCases = Loadable({ | |||
| loader: () => import('./modules/moop_cases/index'), | |||
| loading: Loading, | |||
| }) | |||
| // 课堂讨论 | |||
| // const BoardIndex = Loadable({ | |||
| // loader: () => import('./modules/courses/boards/BoardIndex'), | |||
| // loading:Loading, | |||
| // }) | |||
| // //课堂普通作业&分组作业 | |||
| // const CoursesWorkIndex = Loadable({ | |||
| // loader: () => import('./modules/courses/busyWork/Index'), | |||
| // loading:Loading, | |||
| // }) | |||
| // | |||
| // const TPMShixunchildIndexComponent = Loadable({ | |||
| // loader: () => import('./modules/tpm/shixunchild/ShixunChildIndex'), | |||
| // loading: Loading, | |||
| // }) | |||
| // const TPMshixunfork_listIndexComponent = Loadable({ | |||
| // loader: () => import('./modules/tpm/shixunchild/Shixunfork_list'), | |||
| // loading: Loading, | |||
| // }) | |||
| const ForumsIndexComponent = Loadable({ | |||
| loader: () => import('./modules/forums/ForumsIndex'), | |||
| loading: Loading, | |||
| }) | |||
| // trustie plus forum | |||
| // const TPForumsIndexComponent = Loadable({ | |||
| // loader: () => import('./modules/tp-forums/TPForumsIndex'), | |||
| // loading: Loading, | |||
| // }) | |||
| // const TestPageComponent = Loadable({ | |||
| // loader: () => import('./modules/page/Index'), | |||
| // loading: Loading, | |||
| // }) | |||
| //新建实训 | |||
| const Newshixuns = Loadable({ | |||
| loader: () => import('./modules/tpm/newshixuns/Newshixuns'), | |||
| loading: Loading, | |||
| }) | |||
| //实训首页 | |||
| const ShixunsHome = Loadable({ | |||
| loader: () => import('./modules/home/shixunsHome'), | |||
| loading: Loading, | |||
| }) | |||
| const CompatibilityPageLoadable = Loadable({ | |||
| loader: () => import('./modules/common/CompatibilityPage'), | |||
| loading: Loading, | |||
| }) | |||
| //403页面 | |||
| const Shixunauthority = Loadable({ | |||
| loader: () => import('./modules/403/Shixunauthority'), | |||
| loading: Loading, | |||
| }) | |||
| //404页面 | |||
| const Shixunnopage = Loadable({ | |||
| loader: () => import('./modules/404/Shixunnopage'), | |||
| loading: Loading, | |||
| }) | |||
| //500页面 | |||
| const http500 = Loadable({ | |||
| loader: () => import('./modules/500/http500'), | |||
| loading: Loading, | |||
| }) | |||
| // 登录注册 | |||
| const LoginRegisterPage = Loadable({ | |||
| loader: () => import('./modules/user/LoginRegisterPage'), | |||
| loading: Loading, | |||
| }) | |||
| const AccountPage = Loadable({ | |||
| loader: () => import('./modules/user/AccountPage'), | |||
| loading: Loading, | |||
| }) | |||
| // 个人主页 | |||
| const UsersInfo = Loadable({ | |||
| loader: () => import('./modules/user/usersInfo/Infos'), | |||
| loading: Loading, | |||
| }) | |||
| // 兴趣页面 | |||
| const Interestpage = Loadable({ | |||
| loader: () => import('./modules/login/EducoderInteresse'), | |||
| loading: Loading, | |||
| }) | |||
| //众包创新 | |||
| const ProjectPackages=Loadable({ | |||
| loader: () => import('./modules/projectPackages/ProjectPackageIndex'), | |||
| loading: Loading, | |||
| }) | |||
| class App extends Component { | |||
| constructor(props) { | |||
| super(props) | |||
| // this.state = { | |||
| // isRenders:false, | |||
| // } | |||
| } | |||
| componentDidMount() { | |||
| // force an update if the URL changes | |||
| history.listen(() => { | |||
| this.forceUpdate() | |||
| const $ = window.$ | |||
| // https://www.trustie.net/issues/21919 可能会有问题 | |||
| $("html").animate({ scrollTop: $('html').scrollTop() - 0 }) | |||
| }); | |||
| initAxiosInterceptors(this.props) | |||
| // | |||
| // axios.interceptors.response.use((response) => { | |||
| // // console.log("response"+response); | |||
| // if(response!=undefined) | |||
| // // console.log("response"+response.data.statu); | |||
| // if (response&&response.data.status === 407) { | |||
| // this.setState({ | |||
| // isRenders: true, | |||
| // }) | |||
| // } | |||
| // return response; | |||
| // }, (error) => { | |||
| // //TODO 这里如果样式变了会出现css不加载的情况 | |||
| // }); | |||
| } | |||
| //修改登录方法 | |||
| Modifyloginvalue=()=>{ | |||
| this.setState({ | |||
| isRender:false, | |||
| }) | |||
| } | |||
| render() { | |||
| return ( | |||
| <LocaleProvider locale={zhCN}> | |||
| <MuiThemeProvider theme={theme}> | |||
| <LoginDialog {...this.props} {...this.state} Modifyloginvalue={()=>this.Modifyloginvalue()}></LoginDialog> | |||
| <Notcompletedysl {...this.props} {...this.state}></Notcompletedysl> | |||
| <Trialapplicationysl {...this.props} {...this.state}></Trialapplicationysl> | |||
| <Trialapplicationreview {...this.props} {...this.state}></Trialapplicationreview> | |||
| <Addcourses {...this.props} {...this.state}/> | |||
| <AccountProfile {...this.props} {...this.state}/> | |||
| {/*{*/} | |||
| {/* isRender === true?*/} | |||
| {/* <LoginDialog></LoginDialog> : ""*/} | |||
| {/*}*/} | |||
| {/*{*/} | |||
| {/* isRenders === true?*/} | |||
| {/*<Trialapplication></Trialapplication>*/} | |||
| {/*:""*/} | |||
| {/*}*/} | |||
| <Router> | |||
| <Switch> | |||
| {/*<Route path="/login" component={LoginRegisterPage}/>*/} | |||
| {/*众包创新*/} | |||
| <Route path={"/crowdsourcings"} component={ProjectPackages}/> | |||
| {/*认证*/} | |||
| <Route path="/account" component={AccountPage}/> | |||
| {/*403*/} | |||
| <Route path="/403" component={Shixunauthority}/> | |||
| <Route path="/500" component={http500}/> | |||
| {/*404*/} | |||
| <Route path="/nopage" component={Shixunnopage}/> | |||
| <Route path="/compatibility" component={CompatibilityPageLoadable}/> | |||
| <Route | |||
| path="/login" component={EducoderLogin} | |||
| /> | |||
| <Route | |||
| path="/register" component={EducoderLogin} | |||
| /> | |||
| <Route path="/users/:username" | |||
| render={ | |||
| (props) => (<UsersInfo {...this.props} {...props} {...this.state} />) | |||
| }></Route> | |||
| {/*<Route*/} | |||
| {/* path="/trialapplication" component={Trialapplication}*/} | |||
| {/*/>*/} | |||
| <Route | |||
| path="/changepassword" component={EducoderLogin} | |||
| /> | |||
| <Route | |||
| path="/interesse" component={Interestpage} | |||
| /> | |||
| <Route path="/shixuns/new" component={Newshixuns}> | |||
| </Route> | |||
| <Route path="/tasks/:stageId" component={IndexWrapperComponent}/> | |||
| <Route path="/shixuns/:shixunId" component={TPMIndexComponent}> | |||
| </Route> | |||
| {/*列表页*/} | |||
| <Route path="/shixuns" component={TPMShixunsIndexComponent}/> | |||
| {/* <Route path="/shixunchild" component={TPMShixunchildIndexComponent}> | |||
| </Route> | |||
| <Route path="/fork_list" component={TPMshixunfork_listIndexComponent}> | |||
| </Route> */} | |||
| {/*<Route path="/forums" component={ForumsIndexComponent}>*/} | |||
| {/*</Route>*/} | |||
| {/*实训课程(原实训路径)*/} | |||
| <Route path="/paths" component={ShixunPaths}></Route> | |||
| <Route path="/search" | |||
| render={ | |||
| (props)=>(<SearchPage {...this.props} {...props} {...this.state}></SearchPage>) | |||
| } | |||
| ></Route> | |||
| {/*课堂*/} | |||
| <Route path="/courses" component={CoursesIndex} {...this.props}></Route> | |||
| {/* 课堂讨论 */} | |||
| {/* <Route path="/board" component = {BoardIndex} {...this.props}></Route> */} | |||
| {/* <Route path="/tpforums" component={TPForumsIndexComponent}> | |||
| </Route> */} | |||
| {/* <Route path="/myshixuns/:shixunId/stages/:stageId" component={Index}/> */} | |||
| {/* 兴趣页面*/} | |||
| {/*<Route path="/interest" component={Interestpage}/>*/} | |||
| <Route path="/comment" component={CommentComponent}/> | |||
| <Route path="/testMaterial" component={TestMaterialDesignComponent}/> | |||
| <Route path="/test" component={TestIndex}/> | |||
| <Route path="/testCodeMirror" component={TestCodeMirrorComponent}/> | |||
| <Route path="/testRCComponent" component={TestComponent}/> | |||
| <Route path="/testUrlQuery" component={TestUrlQueryComponent}/> | |||
| {/* 教学案例 */} | |||
| <Route path="/moop_cases"render={ | |||
| (props) => (<MoopCases {...this.props} {...props} {...this.state} />) | |||
| }/> | |||
| {/* <Route component={NotFoundPage}/> */} | |||
| {/*列表页*/} | |||
| {/*<Route component={TPMShixunsIndexComponent}/>*/} | |||
| {/*首页*/} | |||
| <Route exact path="/" component={ShixunsHome}/> | |||
| <Route component={Shixunnopage}/> | |||
| {/*<Route component={ShixunsHome}/>*/} | |||
| </Switch> | |||
| </Router> | |||
| </MuiThemeProvider> | |||
| </LocaleProvider> | |||
| ); | |||
| } | |||
| } | |||
| // moment国际化,设置为中文 | |||
| moment.defineLocale('zh-cn', { | |||
| months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split('_'), | |||
| monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'), | |||
| weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'), | |||
| weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'), | |||
| weekdaysMin: '日_一_二_三_四_五_六'.split('_'), | |||
| longDateFormat: { | |||
| LT: 'Ah点mm分', | |||
| LTS: 'Ah点m分s秒', | |||
| L: 'YYYY-MM-DD', | |||
| LL: 'YYYY年MMMD日', | |||
| LLL: 'YYYY年MMMD日Ah点mm分', | |||
| LLLL: 'YYYY年MMMD日ddddAh点mm分', | |||
| l: 'YYYY-MM-DD', | |||
| ll: 'YYYY年MMMD日', | |||
| lll: 'YYYY年MMMD日Ah点mm分', | |||
| llll: 'YYYY年MMMD日ddddAh点mm分' | |||
| }, | |||
| meridiemParse: /凌晨|早上|上午|中午|下午|晚上/, | |||
| meridiemHour: function (hour, meridiem) { | |||
| if (hour === 12) { | |||
| hour = 0; | |||
| } | |||
| if (meridiem === '凌晨' || meridiem === '早上' || | |||
| meridiem === '上午') { | |||
| return hour; | |||
| } else if (meridiem === '下午' || meridiem === '晚上') { | |||
| return hour + 12; | |||
| } else { | |||
| // '中午' | |||
| return hour >= 11 ? hour : hour + 12; | |||
| } | |||
| }, | |||
| meridiem: function (hour, minute, isLower) { | |||
| var hm = hour * 100 + minute; | |||
| if (hm < 600) { | |||
| return '凌晨'; | |||
| } else if (hm < 900) { | |||
| return '早上'; | |||
| } else if (hm < 1130) { | |||
| return '上午'; | |||
| } else if (hm < 1230) { | |||
| return '中午'; | |||
| } else if (hm < 1800) { | |||
| return '下午'; | |||
| } else { | |||
| return '晚上'; | |||
| } | |||
| }, | |||
| calendar: { | |||
| sameDay: function () { | |||
| return this.minutes() === 0 ? '[今天]Ah[点整]' : '[今天]LT'; | |||
| }, | |||
| nextDay: function () { | |||
| return this.minutes() === 0 ? '[明天]Ah[点整]' : '[明天]LT'; | |||
| }, | |||
| lastDay: function () { | |||
| return this.minutes() === 0 ? '[昨天]Ah[点整]' : '[昨天]LT'; | |||
| }, | |||
| nextWeek: function () { | |||
| var startOfWeek, prefix; | |||
| startOfWeek = moment().startOf('week'); | |||
| prefix = this.unix() - startOfWeek.unix() >= 7 * 24 * 3600 ? '[下]' : '[本]'; | |||
| return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; | |||
| }, | |||
| lastWeek: function () { | |||
| var startOfWeek, prefix; | |||
| startOfWeek = moment().startOf('week'); | |||
| prefix = this.unix() < startOfWeek.unix() ? '[上]' : '[本]'; | |||
| return this.minutes() === 0 ? prefix + 'dddAh点整' : prefix + 'dddAh点mm'; | |||
| }, | |||
| sameElse: 'LL' | |||
| }, | |||
| ordinalParse: /\d{1,2}(日|月|周)/, | |||
| ordinal: function (number, period) { | |||
| switch (period) { | |||
| case 'd': | |||
| case 'D': | |||
| case 'DDD': | |||
| return number + '日'; | |||
| case 'M': | |||
| return number + '月'; | |||
| case 'w': | |||
| case 'W': | |||
| return number + '周'; | |||
| default: | |||
| return number; | |||
| } | |||
| }, | |||
| relativeTime: { | |||
| future: '%s内', | |||
| past: '%s前', | |||
| s: '几秒', | |||
| m: '1分钟', | |||
| mm: '%d分钟', | |||
| h: '1小时', | |||
| hh: '%d小时', | |||
| d: '1天', | |||
| dd: '%d天', | |||
| M: '1个月', | |||
| MM: '%d个月', | |||
| y: '1年', | |||
| yy: '%d年' | |||
| }, | |||
| week: { | |||
| // GB/T 7408-1994《数据元和交换格式·信息交换·日期和时间表示法》与ISO 8601:1988等效 | |||
| dow: 1, // Monday is the first day of the week. | |||
| doy: 4 // The week that contains Jan 4th is the first week of the year. | |||
| } | |||
| }); | |||
| export default SnackbarHOC()(App); | |||
| @@ -1,4 +1,35 @@ | |||
| # Changelog | |||
| ## [v3.2.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 | |||
| ### ENHANCEMENTS | |||
| * ADD 集成邮件和平台站内信等通知系统 | |||
| * Fix 代码库二级页面-优化文件子目录浏览功能(#50388) | |||
| * Fix 代码库二级页面-优化commit提交详情页页面排版及数据显示(#50372) | |||
| * Fix 代码库二级页面-优化commit提交信息列表页加载方式和数据排序功能(#50348) | |||
| * Fix 代码库二级页面-优化创建发行版功能(#50346) | |||
| * Fix 代码库二级页面-优化标签列表页功能(#50344) | |||
| * Fix 代码库二级页面-优化发行版本列表页功能(#50345) | |||
| * Fix 代码库二级页面-优化分支列表页功能(#50343) | |||
| * Fix 其他问题优化(#51581) (#51343) (#51108) | |||
| --- | |||
| ### BUGFIXES | |||
| * Fix 发行版—标签跳转链接错误(#51666) | |||
| * Fix 文件预览报错(#51660) | |||
| * Fix 标签创建时间显示错误(#51658) | |||
| * Fix 分支列表中头像显示问题(#51656) | |||
| * Fix 文本信息过长(#51630) (#51626) | |||
| * Fix 版本库中附件下载400(#51625) | |||
| * Fix loading页面优化(#51588) | |||
| * Fix 提交详情页面优化(#51577) | |||
| * Fix 修复易修复制功能(#51569) | |||
| * Fix 修复新建发行版用户信息显示错误的问题(#51665) | |||
| * Fix 修复查看文件详细信息报错的问题(#51561) | |||
| * Fix 修复提交记录中时间显示格式问题(#51526) | |||
| * Fix 组织下项目更加更新时间倒序排序(#50833) | |||
| ## [v3.1.0](https://forgeplus.trustie.net/projects/jasder/forgeplus/releases) - 2021-06-09 | |||
| * ENHANCEMENTS | |||
| @@ -51,6 +51,51 @@ http://localhost:3000/api/accounts/remote_register | jq | |||
| |-- token |string|用户token| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success", | |||
| "user": { | |||
| "id": 36400, | |||
| "token": "8c87a80d9cfacc92fcb2451845104f35119eda96" | |||
| } | |||
| } | |||
| ``` | |||
| --- | |||
| #### 独立注册接口 | |||
| ``` | |||
| POST accounts/register | |||
| ``` | |||
| *示例* | |||
| ```bash | |||
| curl -X POST \ | |||
| -d "login=2456233122@qq.com" \ | |||
| -d "password=djs_D_00001" \ | |||
| -d "namespace=16895620" \ | |||
| -d "code=forge" \ | |||
| http://localhost:3000/api/accounts/remote_register | jq | |||
| ``` | |||
| *请求参数说明:* | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |login |是|string |邮箱或者手机号 | | |||
| |namespace |是|string |登录名 | | |||
| |password |是|string |密码 | | |||
| |code |是|string |验证码 | | |||
| *返回参数说明:* | |||
| |参数名|类型|说明| | |||
| |-|-|-| | |||
| |user|json object |返回数据| | |||
| |-- id |int |用户id | | |||
| |-- token |string|用户token| | |||
| 返回值 | |||
| ```json | |||
| { | |||
| @@ -338,10 +383,10 @@ http://localhost:3000/api/projects/ | jq | |||
| |-|-|-|-| | |||
| |user_id |是|int |用户id或者组织id | | |||
| |name |是|string |项目名称 | | |||
| |description |是|string |项目描述 | | |||
| |description |否|string |项目描述 | | |||
| |repository_name |是|string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 | | |||
| |project_category_id|是|int |项目类别id | | |||
| |project_language_id|是|int |项目语言id | | |||
| |project_category_id|否|int |项目类别id | | |||
| |project_language_id|否|int |项目语言id | | |||
| |ignore_id |否|int |gitignore相关id | | |||
| |license_id |否|int |开源许可证id | | |||
| |private |否|boolean|项目是否私有, true:为私有,false: 公开,默认为公开 | | |||
| @@ -374,9 +419,7 @@ curl -X POST \ | |||
| -d "user_id=36408" \ | |||
| -d "clone_addr=https://gitea.com/mx8090alex/golden.git" \ | |||
| -d "name=golden_mirror1" \ | |||
| -d "description=golden_mirror" \ | |||
| -d "project_category_id=1" \ | |||
| -d "project_language_id=2" \ | |||
| -d "repository_name=golden_mirror1" \ | |||
| http://localhost:3000/api/projects/migrate.json | jq | |||
| ``` | |||
| *请求参数说明:* | |||
| @@ -388,8 +431,8 @@ http://localhost:3000/api/projects/migrate.json | jq | |||
| |clone_addr |是|string |镜像项目clone地址 | | |||
| |description |否|string |项目描述 | | |||
| |repository_name |是|string |仓库名称, 只含有数字、字母、下划线不能以下划线开头和结尾,且唯一 | | |||
| |project_category_id|是|int |项目类别id | | |||
| |project_language_id|是|int |项目语言id | | |||
| |project_category_id|否|int |项目类别id | | |||
| |project_language_id|否|int |项目语言id | | |||
| |is_mirror |否|boolean|是否设置为镜像, true:是, false:否,默认为否 | | |||
| |auth_username |否|string|镜像源仓库的登录用户名 | | |||
| |auth_password |否|string|镜像源仓库的登录秘密 | | |||
| @@ -99,3 +99,38 @@ $(document).on("turbolinks:before-cache", function () { | |||
| $(function () { | |||
| }); | |||
| $(document).on('turbolinks:load', function() { | |||
| $('.logo-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| $('.attachment-item-left').on("change", 'input[type="file"]', function () { | |||
| var $fileInput = $(this); | |||
| var file = this.files[0]; | |||
| var imageType = /image.*/; | |||
| if (file && file.type.match(imageType)) { | |||
| var reader = new FileReader(); | |||
| reader.onload = function () { | |||
| var $box = $fileInput.parent(); | |||
| $box.find('img').attr('src', reader.result).css('display', 'block'); | |||
| $box.addClass('has-img'); | |||
| }; | |||
| reader.readAsDataURL(file); | |||
| } else { | |||
| } | |||
| }); | |||
| }) | |||
| @@ -1,7 +1,7 @@ | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-courses-index-page').length > 0) { | |||
| let searchContainer = $(".course-list-form"); | |||
| let searchForm = $("form.search-form",searchContainer); | |||
| var searchContainer = $(".course-list-form"); | |||
| var searchForm = $("form.search-form",searchContainer); | |||
| searchContainer.on('change', '.course-homepage-show', function(){ | |||
| searchForm.find('input[type="submit"]').trigger('click'); | |||
| @@ -0,0 +1,141 @@ | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.project-list-container').on('click', '.recommend-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unrecommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为推荐项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: true, | |||
| recommend_index: 1 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $editAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unrecommend-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.recommend-action'); | |||
| var $editAction = $closeAction.siblings('.edit-recommend-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该推荐项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| recommend: false, | |||
| recommend_index: 0 | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $editAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(5).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| // close user | |||
| $('.project-list-container').on('click', '.pinned-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unpinned-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认将该项目设置为精选项目吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: true, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.project-list-container').on('click', '.unpinned-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.pinned-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消该精选项目吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/projects/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| project: { | |||
| is_pinned: false, | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".project-item-"+keywordID).children('td').eq(4).text("") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| @@ -0,0 +1,76 @@ | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/reversed_keywords/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.reversed-keyword-list-container').on('click', '.close-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unclose-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认关闭限制吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/reversed_keywords/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| reversed_keyword: { | |||
| closed: true | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.reversed-keyword-list-container').on('click', '.unclose-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.close-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认开启限制吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/reversed_keywords/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| reversed_keyword: { | |||
| closed: false | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".reversed-keyword-item-"+keywordID).children('td').eq(3).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| @@ -1,7 +1,15 @@ | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-07-16 11:58:16 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:48:59 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/shixun_settings/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function() { | |||
| if ($('body.admins-shixun-settings-index-page').length > 0) { | |||
| let searchContainer = $(".shixun-settings-list-form"); | |||
| let searchForm = $("form.search-form",searchContainer); | |||
| var searchContainer = $(".shixun-settings-list-form"); | |||
| var searchForm = $("form.search-form",searchContainer); | |||
| searchContainer.on('change', '.shixun-settings-select', function(){ | |||
| searchForm.find('input[type="submit"]').trigger('click'); | |||
| @@ -0,0 +1,76 @@ | |||
| /* | |||
| * @Description: Do not edit | |||
| * @Date: 2021-08-31 11:16:45 | |||
| * @LastEditors: viletyy | |||
| * @Author: viletyy | |||
| * @LastEditTime: 2021-08-31 14:19:46 | |||
| * @FilePath: /forgeplus/app/assets/javascripts/admins/system_notifications/index.js | |||
| */ | |||
| $(document).on('turbolinks:load', function(){ | |||
| var showSuccessNotify = function() { | |||
| $.notify({ | |||
| message: '操作成功' | |||
| },{ | |||
| type: 'success' | |||
| }); | |||
| } | |||
| // close user | |||
| $('.system-notification-list-container').on('click', '.close-action', function(){ | |||
| var $closeAction = $(this); | |||
| var $uncloseAction = $closeAction.siblings('.unclose-action'); | |||
| var keywordID = $closeAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认取消置顶吗?', | |||
| ok: function(){ | |||
| $.ajax({ | |||
| url: '/admins/system_notifications/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| system_notification: { | |||
| is_top: false | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.hide(); | |||
| $uncloseAction.show(); | |||
| $(".system-notification-item-"+keywordID).children('td').eq(3).text("") | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| }); | |||
| // unclose user | |||
| $('.system-notification-list-container').on('click', '.unclose-action', function(){ | |||
| var $uncloseAction = $(this); | |||
| var $closeAction = $uncloseAction.siblings('.close-action'); | |||
| var keywordID = $uncloseAction.data('id'); | |||
| customConfirm({ | |||
| content: '确认置顶吗?', | |||
| ok: function () { | |||
| $.ajax({ | |||
| url: '/admins/system_notifications/' + keywordID, | |||
| method: 'PUT', | |||
| dataType: 'json', | |||
| data: { | |||
| system_notification: { | |||
| is_top: true | |||
| } | |||
| }, | |||
| success: function() { | |||
| showSuccessNotify(); | |||
| $closeAction.show(); | |||
| $uncloseAction.hide(); | |||
| $(".system-notification-item-"+keywordID).children('td').eq(3).text("√") | |||
| } | |||
| }); | |||
| } | |||
| }) | |||
| }); | |||
| }) | |||
| @@ -58,3 +58,149 @@ input.form-control { | |||
| position: absolute; | |||
| } | |||
| .logo-item { | |||
| display: flex; | |||
| &-img { | |||
| display: block; | |||
| width: 80px; | |||
| height: 80px; | |||
| background: #e9ecef; | |||
| } | |||
| &-upload { | |||
| cursor: pointer; | |||
| position: absolute; | |||
| top: 0; | |||
| width: 80px; | |||
| height: 80px; | |||
| background: #e9ecef; | |||
| border: 1px solid #ced4da; | |||
| &::before { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 27px; | |||
| left: 39px; | |||
| width: 2px; | |||
| height: 26px; | |||
| background: #495057; | |||
| } | |||
| &::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 39px; | |||
| left: 27px; | |||
| width: 26px; | |||
| height: 2px; | |||
| background: #495057; | |||
| } | |||
| } | |||
| &-left { | |||
| position: relative; | |||
| width: 80px; | |||
| height: 80px; | |||
| &.has-img { | |||
| .logo-item-upload { | |||
| display: none; | |||
| } | |||
| &:hover { | |||
| .logo-item-upload { | |||
| display: block; | |||
| background: rgba(145, 145, 145, 0.8); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &-right { | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| color: #777777; | |||
| font-size: 0.8rem; | |||
| } | |||
| &-title { | |||
| color: #23272B; | |||
| font-size: 1rem; | |||
| } | |||
| } | |||
| .attachment-item { | |||
| display: flex; | |||
| &-img { | |||
| display: block; | |||
| width: 160px; | |||
| height: 160px; | |||
| background: #e9ecef; | |||
| } | |||
| &-upload { | |||
| cursor: pointer; | |||
| position: absolute; | |||
| top: 0; | |||
| width: 160px; | |||
| height: 160px; | |||
| background: #e9ecef; | |||
| border: 1px solid #ced4da; | |||
| &::before { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 54px; | |||
| left: 78px; | |||
| width: 2px; | |||
| height: 52px; | |||
| background: #495057; | |||
| } | |||
| &::after { | |||
| content: ''; | |||
| position: absolute; | |||
| top: 78px; | |||
| left: 54px; | |||
| width: 52px; | |||
| height: 2px; | |||
| background: #495057; | |||
| } | |||
| } | |||
| &-left { | |||
| position: relative; | |||
| width: 160px; | |||
| height: 160px; | |||
| &.has-img { | |||
| .attachment-item-upload { | |||
| display: none; | |||
| } | |||
| &:hover { | |||
| .attachment-item-upload { | |||
| display: block; | |||
| background: rgba(145, 145, 145, 0.8); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &-right { | |||
| padding-top: 100px; | |||
| display: flex; | |||
| flex-direction: column; | |||
| justify-content: space-between; | |||
| color: #777777; | |||
| font-size: 0.8rem; | |||
| } | |||
| &-title { | |||
| color: #23272B; | |||
| font-size: 1rem; | |||
| } | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| class AccountsController < ApplicationController | |||
| include ApplicationHelper | |||
| #skip_before_action :check_account, :only => [:logout] | |||
| @@ -9,6 +10,7 @@ class AccountsController < ApplicationController | |||
| # 其他平台同步注册的用户 | |||
| def remote_register | |||
| username = params[:username]&.gsub(/\s+/, "") | |||
| tip_exception("无法使用以下关键词:#{username},请重新命名") if ReversedKeyword.check_exists?(username) | |||
| email = params[:email]&.gsub(/\s+/, "") | |||
| password = params[:password] | |||
| platform = (params[:platform] || 'forge')&.gsub(/\s+/, "") | |||
| @@ -108,60 +110,46 @@ class AccountsController < ApplicationController | |||
| # 用户注册 | |||
| # 注意:用户注册需要兼顾本地版,本地版是不需要验证码及激活码以及使用授权的,注册完成即可使用 | |||
| # params[:login] 邮箱或者手机号 | |||
| # params[:namespace] 登录名 | |||
| # params[:code] 验证码 | |||
| # code_type 1:注册手机验证码 8:邮箱注册验证码 | |||
| # 本地forge注册入口 | |||
| # 本地forge注册入口需要重新更改逻辑 | |||
| def register | |||
| # type只可能是1或者8 | |||
| user = nil | |||
| begin | |||
| # 查询验证码是否正确;type只可能是1或者8 | |||
| type = phone_mail_type(params[:login].strip) | |||
| # code = params[:code].strip | |||
| if type == 1 | |||
| uid_logger("start register by phone: type is #{type}") | |||
| pre = 'p' | |||
| email = nil | |||
| phone = params[:login] | |||
| # verifi_code = VerificationCode.where(phone: phone, code: code, code_type: 1).last | |||
| # TODO: 暂时限定邮箱注册 | |||
| return normal_status(-1, '只支持邮箱注册') | |||
| else | |||
| uid_logger("start register by email: type is #{type}") | |||
| pre = 'm' | |||
| email = params[:login] | |||
| phone = nil | |||
| return normal_status(-1, "该邮箱已注册") if User.exists?(mail: params[:login]) | |||
| return normal_status(-1, "邮箱格式错误") unless params[:login] =~ CustomRegexp::EMAIL | |||
| # verifi_code = VerificationCode.where(email: email, code: code, code_type: 8).last | |||
| end | |||
| # uid_logger("start register: verifi_code is #{verifi_code}, code is #{code}, time is #{Time.now.to_i - verifi_code.try(:created_at).to_i}") | |||
| # check_code = (verifi_code.try(:code) == code.strip && (Time.now.to_i - verifi_code.created_at.to_i) <= 10*60) | |||
| # todo 上线前请删除万能验证码"513231" | |||
| return normal_status(-1, "8~16位密码,支持字母数字和符号") unless params[:password] =~ CustomRegexp::PASSWORD | |||
| code = generate_identifier User, 8, pre | |||
| login = pre + code | |||
| @user = User.new(admin: false, login: login, mail: email, phone: phone, type: "User") | |||
| @user.password = params[:password] | |||
| # 现在因为是验证码,所以在注册的时候就可以激活 | |||
| @user.activate | |||
| # 必须要用save操作,密码的保存是在users中 | |||
| interactor = Gitea::RegisterInteractor.call({username: login, email: email, password: params[:password]}) | |||
| Register::Form.new(register_params).validate! | |||
| user = Users::RegisterService.call(register_params) | |||
| password = register_params[:password].strip | |||
| # gitea用户注册, email, username, password | |||
| interactor = Gitea::RegisterInteractor.call({username: user.login, email: user.mail, password: password}) | |||
| if interactor.success? | |||
| gitea_user = interactor.result | |||
| result = Gitea::User::GenerateTokenService.new(login, params[:password]).call | |||
| @user.gitea_token = result['sha1'] | |||
| @user.gitea_uid = gitea_user[:body]['id'] | |||
| if @user.save! | |||
| UserExtension.create!(user_id: @user.id) | |||
| successful_authentication(@user) | |||
| normal_status("注册成功") | |||
| result = Gitea::User::GenerateTokenService.call(user.login, password) | |||
| user.gitea_token = result['sha1'] | |||
| user.gitea_uid = gitea_user[:body]['id'] | |||
| if user.save! | |||
| UserExtension.create!(user_id: user.id) | |||
| successful_authentication(user) | |||
| render_ok | |||
| end | |||
| else | |||
| tip_exception(-1, interactor.error) | |||
| end | |||
| rescue Register::BaseForm::EmailError => e | |||
| render_error(-2, e.message) | |||
| rescue Register::BaseForm::LoginError => e | |||
| render_error(-3, e.message) | |||
| rescue Register::BaseForm::PhoneError => e | |||
| render_error(-4, e.message) | |||
| rescue Register::BaseForm::PasswordFormatError => e | |||
| render_error(-5, e.message) | |||
| rescue Register::BaseForm::VerifiCodeError => e | |||
| render_error(-6, e.message) | |||
| rescue Exception => e | |||
| Gitea::User::DeleteService.call(user.login) unless user.nil? | |||
| uid_logger_error(e.message) | |||
| tip_exception(-1, e.message) | |||
| end | |||
| @@ -296,7 +284,7 @@ class AccountsController < ApplicationController | |||
| # 发送验证码 | |||
| # params[:login] 手机号或者邮箱号 | |||
| # params[:type]为事件通知类型 1:用户注册注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 | |||
| # params[:type]为事件通知类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验收手机号有效 # 如果有新的继续后面加 | |||
| # 发送验证码:send_type 1:注册手机验证码 2:找回密码手机验证码 3:找回密码邮箱验证码 4:绑定手机 5:绑定邮箱 | |||
| # 6:手机验证码登录 7:邮箱验证码登录 8:邮箱注册验证码 9: 验收手机号有效 | |||
| def get_verification_code | |||
| @@ -310,19 +298,22 @@ class AccountsController < ApplicationController | |||
| sign = Digest::MD5.hexdigest("#{OPENKEY}#{value}") | |||
| tip_exception(501, "请求不合理") if sign != params[:smscode] | |||
| logger.info "########### 验证码:#{verification_code}" | |||
| logger.info("########get_verification_code: login_type: #{login_type}, send_type:#{send_type}, ") | |||
| # 记录验证码 | |||
| check_verification_code(verification_code, send_type, value) | |||
| sucess_status | |||
| render_ok | |||
| end | |||
| # 1 手机类型;0 邮箱类型 | |||
| # 注意新版的login是自动名生成的 | |||
| def phone_mail_type value | |||
| value =~ /^1\d{10}$/ ? 1 : 0 | |||
| # check user's login or email or phone is used | |||
| # params[:value] 手机号或者邮箱号或者登录名 | |||
| # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) | |||
| def check | |||
| Register::CheckColumnsForm.new(check_params).validate! | |||
| render_ok | |||
| end | |||
| private | |||
| # type 事件类型 1:用户注册 2:忘记密码 3: 绑定手机 4: 绑定邮箱, 5: 验证手机号是否有效 # 如果有新的继续后面加 | |||
| @@ -368,4 +359,13 @@ class AccountsController < ApplicationController | |||
| def account_params | |||
| params.require(:account).permit(:login, :password) | |||
| end | |||
| def check_params | |||
| params.permit(:type, :value) | |||
| end | |||
| def register_params | |||
| params.permit(:login, :namespace, :password, :code) | |||
| end | |||
| end | |||
| @@ -0,0 +1,55 @@ | |||
| class Admins::EduSettingsController < Admins::BaseController | |||
| before_action :find_setting, only: [:edit,:update, :destroy] | |||
| def index | |||
| default_sort('id', 'desc') | |||
| edu_settings = Admins::EduSettingQuery.call(params) | |||
| @edu_settings = paginate edu_settings | |||
| end | |||
| def new | |||
| @edu_setting = EduSetting.new | |||
| end | |||
| def edit | |||
| end | |||
| def create | |||
| @edu_setting = EduSetting.new(edu_setting_params) | |||
| if @edu_setting.save | |||
| redirect_to admins_edu_settings_path | |||
| flash[:success] = '创建成功' | |||
| else | |||
| redirect_to admins_edu_settings_path | |||
| flash[:danger] = @edu_setting.errors.full_messages.join(",") | |||
| end | |||
| end | |||
| def update | |||
| if @edu_setting.update!(edu_setting_params) | |||
| flash[:success] = '更新成功' | |||
| else | |||
| flash[:danger] = @edu_setting.errors.full_messages.join(",") | |||
| end | |||
| redirect_to admins_edu_settings_path | |||
| end | |||
| def destroy | |||
| if @edu_setting.destroy! | |||
| flash[:success] = '删除成功' | |||
| else | |||
| lash[:danger] = '删除失败' | |||
| end | |||
| redirect_to admins_edu_settings_path | |||
| end | |||
| private | |||
| def find_setting | |||
| @edu_setting ||= EduSetting.find(params[:id]) | |||
| end | |||
| def edu_setting_params | |||
| params.require(:edu_setting).permit(:name, :value, :description) | |||
| end | |||
| end | |||
| @@ -0,0 +1,44 @@ | |||
| class Admins::MessageTemplatesController < Admins::BaseController | |||
| before_action :get_template, only: [:edit, :update, :destroy] | |||
| def index | |||
| message_templates = MessageTemplate.group(:type).count.keys | |||
| @message_templates = kaminari_array_paginate(message_templates) | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| if @message_template.update_attributes(message_template_params) | |||
| redirect_to admins_message_templates_path | |||
| flash[:success] = '消息模版更新成功' | |||
| else | |||
| redirect_to admins_message_templates_path | |||
| flash[:danger] = @message_template.errors.full_messages.join(",") | |||
| end | |||
| end | |||
| def init_data | |||
| if MessageTemplate.build_init_data | |||
| redirect_to admins_message_templates_path | |||
| flash[:success] = '消息模版初始化成功' | |||
| else | |||
| redirect_to admins_message_templates_path | |||
| flash[:danger] = '消息模版初始化失败' | |||
| end | |||
| end | |||
| private | |||
| def message_template_params | |||
| params.require(@message_template.type.split("::").join("_").underscore.to_sym).permit! | |||
| end | |||
| def get_template | |||
| @message_template = MessageTemplate.find_by(id: params[:id]) | |||
| unless @message_template.present? | |||
| redirect_to admins_message_templates_path | |||
| flash[:danger] = "消息模版不存在" | |||
| end | |||
| end | |||
| end | |||
| @@ -22,7 +22,7 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| max_position_items = ProjectCategory.select(:id, :position).pluck(:position).reject!(&:blank?) | |||
| max_position = max_position_items.present? ? max_position_items.max.to_i : 0 | |||
| @project_category = ProjectCategory.new(name: @name,position: max_position) | |||
| @project_category = ProjectCategory.new(name: @name,position: max_position, pinned_index: params[:project_category][:pinned_index].to_i) | |||
| if @project_category.save | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = '创建成功' | |||
| @@ -33,17 +33,18 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| end | |||
| def update | |||
| if @project_category.update_attribute(:name, @name) | |||
| if @project_category.update_attributes({name: @name, pinned_index: params[:project_category][:pinned_index].to_i}) | |||
| save_image_file(params[:logo], 'logo') | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = '更新成功' | |||
| else | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = '更新失败' | |||
| flash[:danger] = '更新失败' | |||
| end | |||
| end | |||
| def destroy | |||
| if @project_language.destroy | |||
| if @project_category.destroy | |||
| redirect_to admins_project_categories_path | |||
| flash[:success] = "删除成功" | |||
| else | |||
| @@ -80,4 +81,12 @@ class Admins::ProjectCategoriesController < Admins::BaseController | |||
| flash[:danger] = '分类已存在' | |||
| end | |||
| end | |||
| def save_image_file(file, type) | |||
| return unless file.present? && file.is_a?(ActionDispatch::Http::UploadedFile) | |||
| file_path = Util::FileManage.source_disk_filename(@project_category, type) | |||
| File.delete(file_path) if File.exist?(file_path) # 删除之前的文件 | |||
| Util.write_file(file, file_path) | |||
| end | |||
| end | |||
| @@ -1,4 +1,5 @@ | |||
| class Admins::ProjectsController < Admins::BaseController | |||
| before_action :find_project, only: [:edit, :update] | |||
| def index | |||
| sort_by = Project.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_on' | |||
| @@ -8,6 +9,26 @@ class Admins::ProjectsController < Admins::BaseController | |||
| @projects = paginate projects.includes(:owner, :members, :issues, :versions, :attachments, :project_score) | |||
| end | |||
| def edit ;end | |||
| def update | |||
| respond_to do |format| | |||
| if @project.update_attributes(project_update_params) | |||
| format.html do | |||
| redirect_to admins_projects_path | |||
| flash[:sucess] = "更新成功" | |||
| end | |||
| format.js {render_ok} | |||
| else | |||
| format.html do | |||
| redirect_to admins_projects_path | |||
| flash[:danger] = "更新失败" | |||
| end | |||
| format.js {render_js_error} | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| project = Project.find_by!(id: params[:id]) | |||
| ActiveRecord::Base.transaction do | |||
| @@ -21,4 +42,13 @@ class Admins::ProjectsController < Admins::BaseController | |||
| redirect_to admins_projects_path | |||
| flash[:danger] = "删除失败" | |||
| end | |||
| private | |||
| def find_project | |||
| @project = Project.find_by_id(params[:id]) | |||
| end | |||
| def project_update_params | |||
| params.require(:project).permit(:is_pinned, :recommend, :recommend_index) | |||
| end | |||
| end | |||
| @@ -0,0 +1,84 @@ | |||
| class Admins::ReversedKeywordsController < Admins::BaseController | |||
| before_action :get_keyword, only: [:edit,:update, :destroy] | |||
| # before_action :validate_identifer, only: [:create, :update] | |||
| def index | |||
| sort_by = ReversedKeyword.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = ReversedKeyword.ransack(identifier_cont: params[:search]) | |||
| keywords = q.result(distinct: true).order("#{sort_by} #{sort_direction}") | |||
| @keywords = paginate(keywords) | |||
| end | |||
| def new | |||
| @keyword = ReversedKeyword.new | |||
| end | |||
| def edit | |||
| end | |||
| def create | |||
| @keyword = ReversedKeyword.new(keyword_params) | |||
| if @keyword.save | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:success] = '系统保留关键词创建成功' | |||
| else | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = @keyword.errors.full_messages.join(",") | |||
| end | |||
| end | |||
| def update | |||
| respond_to do |format| | |||
| if @keyword.update_attributes(keyword_params) | |||
| format.html do | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:success] = '系统保留关键词更新成功' | |||
| end | |||
| format.js {render_ok} | |||
| else | |||
| format.html do | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = @keyword.errors.full_messages.join(",") | |||
| end | |||
| format.js {render_js_error} | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| if @keyword.destroy | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:success] = "系统保留关键词删除成功" | |||
| else | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = "系统保留关键词删除失败" | |||
| end | |||
| end | |||
| private | |||
| def keyword_params | |||
| params.require(:reversed_keyword).permit! | |||
| end | |||
| def get_keyword | |||
| @keyword = ReversedKeyword.find_by(id: params[:id]) | |||
| unless @keyword.present? | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = "系统保留关键词不存在" | |||
| end | |||
| end | |||
| def validate_identifer | |||
| identifer = keyword_params[:identifier].to_s.downcase | |||
| if identifer.blank? | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = '系统保留关键词标识不能为空' | |||
| elsif ProjectLanguage.exists?(name: identifer) | |||
| redirect_to admins_reversed_keywords_path | |||
| flash[:danger] = '系统保留关键词已存在' | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,56 @@ | |||
| class Admins::SitesController < Admins::BaseController | |||
| before_action :find_site, only: [:edit,:update, :destroy] | |||
| def index | |||
| default_sort('id', 'desc') | |||
| sites = Admins::SiteQuery.call(params) | |||
| @sites = paginate sites | |||
| end | |||
| def new | |||
| @site = Site.new | |||
| end | |||
| def edit | |||
| end | |||
| def create | |||
| @site = Site.new(site_params) | |||
| if @site.save | |||
| redirect_to admins_sites_path | |||
| flash[:success] = '创建成功' | |||
| else | |||
| redirect_to admins_sites_path | |||
| flash[:danger] = @site.errors.full_messages.join(",") | |||
| end | |||
| end | |||
| def update | |||
| if @site.update!(site_params) | |||
| flash[:success] = '更新成功' | |||
| else | |||
| flash[:danger] = @site.errors.full_messages.join(",") | |||
| end | |||
| redirect_to admins_sites_path | |||
| end | |||
| def destroy | |||
| if @site.destroy! | |||
| flash[:success] = '删除成功' | |||
| else | |||
| lash[:danger] = '删除失败' | |||
| end | |||
| redirect_to admins_sites_path | |||
| end | |||
| private | |||
| def find_site | |||
| @site ||= Site.find(params[:id]) | |||
| end | |||
| def site_params | |||
| params.require(:site).permit(:name, :url, :key, :site_type) | |||
| end | |||
| end | |||
| @@ -0,0 +1,75 @@ | |||
| class Admins::SystemNotificationsController < Admins::BaseController | |||
| before_action :get_notification, only: [:history, :edit,:update, :destroy] | |||
| # before_action :validate_identifer, only: [:create, :update] | |||
| def index | |||
| sort_by = SystemNotification.column_names.include?(params[:sort_by]) ? params[:sort_by] : 'created_at' | |||
| sort_direction = %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| q = SystemNotification.ransack(subject_cont: params[:search]) | |||
| notifications = q.result(distinct: true).reorder("#{sort_by} #{sort_direction},created_at desc") | |||
| @notifications = paginate(notifications) | |||
| end | |||
| def history | |||
| @users = @notification.users | |||
| end | |||
| def new | |||
| @notification = SystemNotification.new | |||
| end | |||
| def edit | |||
| end | |||
| def create | |||
| @notification = SystemNotification.new(notification_params) | |||
| if @notification.save | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = '系统消息创建成功' | |||
| else | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = @notification.errors.full_messages.join(",") | |||
| end | |||
| end | |||
| def update | |||
| respond_to do |format| | |||
| if @notification.update_attributes(notification_params) | |||
| format.html do | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = '系统消息更新成功' | |||
| end | |||
| format.js {render_ok} | |||
| else | |||
| format.html do | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = @notification.errors.full_messages.join(",") | |||
| end | |||
| format.js {render_js_error} | |||
| end | |||
| end | |||
| end | |||
| def destroy | |||
| if @notification.destroy | |||
| redirect_to admins_system_notifications_path | |||
| flash[:success] = "系统消息删除成功" | |||
| else | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = "系统消息删除失败" | |||
| end | |||
| end | |||
| private | |||
| def notification_params | |||
| params.require(:system_notification).permit! | |||
| end | |||
| def get_notification | |||
| @notification = SystemNotification.find_by(id: params[:id]) | |||
| unless @notification.present? | |||
| redirect_to admins_system_notifications_path | |||
| flash[:danger] = "系统消息不存在" | |||
| end | |||
| end | |||
| end | |||
| @@ -1,4 +1,6 @@ | |||
| class Admins::UsersController < Admins::BaseController | |||
| before_action :finder_user, except: [:index] | |||
| def index | |||
| params[:sort_by] = params[:sort_by].presence || 'created_on' | |||
| params[:sort_direction] = params[:sort_direction].presence || 'desc' | |||
| @@ -8,12 +10,9 @@ class Admins::UsersController < Admins::BaseController | |||
| end | |||
| def edit | |||
| @user = User.find(params[:id]) | |||
| end | |||
| def update | |||
| @user = User.find(params[:id]) | |||
| Admins::UpdateUserService.call(@user, update_params) | |||
| flash[:success] = '保存成功' | |||
| redirect_to edit_admins_user_path(@user) | |||
| @@ -26,43 +25,47 @@ class Admins::UsersController < Admins::BaseController | |||
| end | |||
| def destroy | |||
| User.find(params[:id]).destroy! | |||
| @user.destroy! | |||
| Gitea::User::DeleteService.call(@user.login) | |||
| render_delete_success | |||
| end | |||
| def lock | |||
| User.find(params[:id]).lock! | |||
| @user.lock! | |||
| render_ok | |||
| end | |||
| def unlock | |||
| User.find(params[:id]).activate! | |||
| @user.activate! | |||
| render_ok | |||
| end | |||
| def reward_grade | |||
| user = User.find(params[:user_id]) | |||
| return render_unprocessable_entity('金币数量必须大于0') if params[:grade].to_i <= 0 | |||
| RewardGradeService.call(user, container_id: user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true) | |||
| RewardGradeService.call(@user, container_id: @user.id, container_type: 'Feedback', score: params[:grade].to_i, not_unique: true) | |||
| render_ok(grade: user.grade) | |||
| render_ok(grade: @user.grade) | |||
| end | |||
| def reset_login_times | |||
| User.find(params[:id]).reset_login_times! | |||
| @user.reset_login_times! | |||
| render_ok | |||
| end | |||
| private | |||
| def finder_user | |||
| @user = User.find(params[:id]) | |||
| end | |||
| def update_params | |||
| params.require(:user).permit(%i[lastname nickname gender identity technical_title student_id is_shixun_marker | |||
| mail phone location location_city school_id department_id admin business is_test | |||
| password professional_certification authentication]) | |||
| password professional_certification authentication login]) | |||
| end | |||
| end | |||
| @@ -70,49 +70,11 @@ class ApplicationController < ActionController::Base | |||
| (current_user.professional_certification && (ue.teacher? || ue.professional?)) | |||
| end | |||
| def shixun_marker | |||
| unless current_user.is_shixun_marker? || current_user.admin_or_business? | |||
| tip_exception(403, "..") | |||
| end | |||
| end | |||
| # 实训的访问权限 | |||
| def shixun_access_allowed | |||
| if !current_user.shixun_permission(@shixun) | |||
| tip_exception(403, "..") | |||
| end | |||
| end | |||
| def admin_or_business? | |||
| User.current.admin? || User.current.business? | |||
| end | |||
| # 访问课堂时没权限直接弹加入课堂的弹框 :409 | |||
| def user_course_identity | |||
| @user_course_identity = current_user.course_identity(@course) | |||
| if @user_course_identity > Course::STUDENT && @course.is_public == 0 | |||
| tip_exception(401, "..") unless User.current.logged? | |||
| check_account | |||
| tip_exception(@course.excellent ? 410 : 409, "您没有权限进入") | |||
| end | |||
| if @user_course_identity > Course::CREATOR && @user_course_identity <= Course::STUDENT && @course.tea_id != current_user.id | |||
| # 实名认证和职业认证的身份判断 | |||
| tip_exception(411, "你的实名认证和职业认证审核未通过") if @course.authentication && | |||
| @course.professional_certification && (!current_user.authentication && !current_user.professional_certification) | |||
| tip_exception(411, "你的实名认证审核未通过") if @course.authentication && !current_user.authentication | |||
| tip_exception(411, "你的职业认证审核未通过") if @course.professional_certification && !current_user.professional_certification | |||
| end | |||
| uid_logger("###############user_course_identity:#{@user_course_identity}") | |||
| end | |||
| # 题库的访问权限 | |||
| def bank_visit_auth | |||
| tip_exception(-2,"未通过职业认证") if current_user.is_teacher? && !current_user.certification_teacher? && !current_user.admin_or_business? && @bank.user_id != current_user.id && @bank.is_public | |||
| tip_exception(403, "无权限") unless @bank.user_id == current_user.id || current_user.admin_or_business? || | |||
| (current_user.certification_teacher? && @bank.is_public) | |||
| end | |||
| # 判断用户的邮箱或者手机是否可用 | |||
| # params[:type] 1: 注册;2:忘记密码;3:绑定 | |||
| def check_mail_and_phone_valid login, type | |||
| @@ -120,16 +82,16 @@ class ApplicationController < ActionController::Base | |||
| login =~ /^[a-zA-Z0-9]+([._\\]*[a-zA-Z0-9])$/ | |||
| tip_exception(-2, "请输入正确的手机号或邮箱") | |||
| end | |||
| # 考虑到安全参数问题,多一次查询,去掉Union | |||
| user = User.where(phone: login).first || User.where(mail: login).first | |||
| if type.to_i == 1 && !user.nil? | |||
| user_exist = Owner.exists?(phone: login) || Owner.exists?(mail: login) | |||
| if user_exist && type.to_i == 1 | |||
| tip_exception(-2, "该手机号码或邮箱已被注册") | |||
| elsif type.to_i == 2 && user.nil? | |||
| elsif type.to_i == 2 && !user_exist | |||
| tip_exception(-2, "该手机号码或邮箱未注册") | |||
| elsif type.to_i == 3 && user.present? | |||
| elsif type.to_i == 3 && user_exist | |||
| tip_exception(-2, "该手机号码或邮箱已绑定") | |||
| end | |||
| sucess_status | |||
| render_ok | |||
| end | |||
| # 发送及记录激活码 | |||
| @@ -140,7 +102,7 @@ class ApplicationController < ActionController::Base | |||
| when 1, 2, 4, 9 | |||
| # 手机类型的发送 | |||
| sigle_para = {phone: value} | |||
| status = Educoder::Sms.send(mobile: value, code: code) | |||
| status = Gitlink::Sms.send(mobile: value, code: code) | |||
| tip_exception(-2, code_msg(status)) if status != 0 | |||
| when 8, 3, 5 | |||
| # 邮箱类型的发送 | |||
| @@ -186,26 +148,6 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| end | |||
| def find_course | |||
| return normal_status(2, '缺少course_id参数!') if params[:course_id].blank? | |||
| @course = Course.find(params[:course_id]) | |||
| tip_exception(404, "") if @course.is_delete == 1 && !current_user.admin_or_business? | |||
| rescue Exception => e | |||
| tip_exception(e.message) | |||
| end | |||
| def course_manager | |||
| return normal_status(403, '只有课堂管理员才有权限') if @user_course_identity > Course::CREATOR | |||
| end | |||
| def find_board | |||
| return normal_status(2, "缺少board_id参数") if params[:board_id].blank? | |||
| @board = Board.find(params[:board_id]) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def validate_type(object_type) | |||
| normal_status(2, "参数") if params.has_key?(:sort_type) && !SORT_TYPE.include?(params[:sort_type].strip) | |||
| end | |||
| @@ -215,21 +157,6 @@ class ApplicationController < ActionController::Base | |||
| @page_size = params[:page_size] || 15 | |||
| end | |||
| # 课堂教师权限 | |||
| def teacher_allowed | |||
| logger.info("#####identity: #{current_user.course_identity(@course)}") | |||
| unless current_user.course_identity(@course) < Course::STUDENT | |||
| normal_status(403, "") | |||
| end | |||
| end | |||
| # 课堂教师、课堂管理员、超级管理员的权限(不包含助教) | |||
| def teacher_or_admin_allowed | |||
| unless current_user.course_identity(@course) < Course::ASSISTANT_PROFESSOR | |||
| normal_status(403, "") | |||
| end | |||
| end | |||
| def require_admin | |||
| normal_status(403, "") unless User.current.admin? | |||
| end | |||
| @@ -246,9 +173,17 @@ class ApplicationController < ActionController::Base | |||
| tip_exception(401, "请登录后再操作") unless User.current.logged? | |||
| end | |||
| def require_profile_completed | |||
| tip_exception(411, "请完善资料后再操作") unless User.current.profile_is_completed? | |||
| end | |||
| def require_user_profile_completed(user) | |||
| tip_exception(412, "请用户完善资料后再操作") unless user.profile_is_completed? | |||
| end | |||
| # 异常提醒 | |||
| def tip_exception(status = -1, message) | |||
| raise Educoder::TipException.new(status, message) | |||
| raise Gitlink::TipException.new(status, message) | |||
| end | |||
| def missing_template | |||
| @@ -257,7 +192,7 @@ class ApplicationController < ActionController::Base | |||
| # 弹框提醒 | |||
| def tip_show_exception(status = -2, message) | |||
| raise Educoder::TipException.new(status, message) | |||
| raise Gitlink::TipException.new(status, message) | |||
| end | |||
| def normal_status(status = 0, message) | |||
| @@ -272,7 +207,7 @@ class ApplicationController < ActionController::Base | |||
| # 资料是否完善 | |||
| def check_account | |||
| if !current_user.profile_completed? | |||
| if !current_user. profile_is_completed? | |||
| #info_url = '/account/profile' | |||
| tip_exception(402, nil) | |||
| end | |||
| @@ -337,18 +272,18 @@ class ApplicationController < ActionController::Base | |||
| # 测试版前端需求 | |||
| logger.info("subdomain:#{request.subdomain}") | |||
| if request.subdomain != "www" | |||
| if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 | |||
| User.current = User.find 81403 | |||
| elsif params[:debug] == 'student' | |||
| User.current = User.find 8686 | |||
| elsif params[:debug] == 'admin' | |||
| logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....." | |||
| user = User.find 36480 | |||
| User.current = user | |||
| cookies.signed[:user_id] = user.id | |||
| end | |||
| end | |||
| # if request.subdomain != "www" | |||
| # if params[:debug] == 'teacher' #todo 为了测试,记得讲debug删除 | |||
| # User.current = User.find 81403 | |||
| # elsif params[:debug] == 'student' | |||
| # User.current = User.find 8686 | |||
| # elsif params[:debug] == 'admin' | |||
| # logger.info "@@@@@@@@@@@@@@@@@@@@@@ debug mode....." | |||
| # user = User.find 36480 | |||
| # User.current = user | |||
| # cookies.signed[:user_id] = user.id | |||
| # end | |||
| # end | |||
| # User.current = User.find 81403 | |||
| end | |||
| @@ -438,7 +373,7 @@ class ApplicationController < ActionController::Base | |||
| JSON.parse(res) | |||
| rescue Exception => e | |||
| uid_logger_error("--uri_exec: exception #{e.message}") | |||
| raise Educoder::TipException.new("实训平台繁忙(繁忙等级:84)") | |||
| raise Gitlink::TipException.new("实训平台繁忙(繁忙等级:84)") | |||
| end | |||
| end | |||
| @@ -457,7 +392,7 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| rescue Exception => e | |||
| uid_logger("--uri_exec: exception #{e.message}") | |||
| raise Educoder::TipException.new(message) | |||
| raise Gitlink::TipException.new(message) | |||
| end | |||
| end | |||
| @@ -481,7 +416,7 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| rescue Exception => e | |||
| uid_logger("--uri_exec: exception #{e.message}") | |||
| raise Educoder::TipException.new("服务器繁忙") | |||
| raise Gitlink::TipException.new("服务器繁忙") | |||
| end | |||
| end | |||
| @@ -653,8 +588,8 @@ class ApplicationController < ActionController::Base | |||
| # 获取Oauth Client | |||
| def get_client(site) | |||
| client_id = Rails.configuration.educoder['client_id'] | |||
| client_secret = Rails.configuration.educoder['client_secret'] | |||
| client_id = Rails.configuration.Gitlink['client_id'] | |||
| client_secret = Rails.configuration.Gitlink['client_secret'] | |||
| OAuth2::Client.new(client_id, client_secret, site: site) | |||
| end | |||
| @@ -754,10 +689,10 @@ class ApplicationController < ActionController::Base | |||
| if @project and current_user.can_read_project?(@project) | |||
| logger.info "###########: has project and can read project" | |||
| @project | |||
| elsif @project && current_user.is_a?(AnonymousUser) | |||
| logger.info "###########:This is AnonymousUser" | |||
| @project = nil if !@project.is_public? | |||
| render_forbidden and return | |||
| # elsif @project && current_user.is_a?(AnonymousUser) | |||
| # logger.info "###########:This is AnonymousUser" | |||
| # @project = nil if !@project.is_public? | |||
| # render_forbidden and return | |||
| else | |||
| logger.info "###########:project not found" | |||
| @project = nil | |||
| @@ -771,11 +706,12 @@ class ApplicationController < ActionController::Base | |||
| end | |||
| def base_url | |||
| request.base_url | |||
| Rails.application.config_for(:configuration)['platform_url'] || request.base_url | |||
| end | |||
| def convert_image! | |||
| @image = params[:image] || user_params[:image] | |||
| @image = params[:image] | |||
| @image = @image.nil? && params[:user].present? ? params[:user][:image] : @image | |||
| return unless @image.present? | |||
| max_size = EduSetting.get('upload_avatar_max_size') || 2 * 1024 * 1024 # 2M | |||
| if @image.class == ActionDispatch::Http::UploadedFile | |||
| @@ -846,4 +782,8 @@ class ApplicationController < ActionController::Base | |||
| HotSearchKeyword.add(keyword) | |||
| end | |||
| def find_atme_receivers | |||
| @atme_receivers = User.where(login: params[:receivers_login]) | |||
| end | |||
| end | |||
| @@ -32,8 +32,17 @@ class AttachmentsController < ApplicationController | |||
| def get_file | |||
| normal_status(-1, "参数缺失") if params[:download_url].blank? | |||
| url = URI.encode(params[:download_url].to_s.gsub("http:", "https:")) | |||
| response = Faraday.get(url) | |||
| filename = params[:download_url].to_s.split("/").pop() | |||
| if url.starts_with?(base_url) | |||
| domain = Gitea.gitea_config[:domain] | |||
| api_url = Gitea.gitea_config[:base_url] | |||
| url = url.split(base_url)[1].gsub("api", "repos").gsub('?filepath=', '/').gsub('&', '?') | |||
| request_url = [domain, api_url, url, "?ref=#{params[:ref]}&access_token=#{current_user&.gitea_token}"].join | |||
| response = Faraday.get(request_url) | |||
| filename = url.to_s.split("/").pop() | |||
| else | |||
| response = Faraday.get(url) | |||
| filename = params[:download_url].to_s.split("/").pop() | |||
| end | |||
| send_data(response.body.force_encoding("UTF-8"), filename: filename, type: "application/octet-stream", disposition: 'attachment') | |||
| end | |||
| @@ -187,7 +196,7 @@ class AttachmentsController < ApplicationController | |||
| end | |||
| def file_save_to_ucloud(path, file, content_type) | |||
| ufile = Educoder::Ufile.new( | |||
| ufile = Gitlink::Ufile.new( | |||
| ucloud_public_key: edu_setting('public_key'), | |||
| ucloud_private_key: edu_setting('private_key'), | |||
| ucloud_public_read: true, | |||
| @@ -6,26 +6,48 @@ class CompareController < ApplicationController | |||
| end | |||
| def show | |||
| load_compare_params | |||
| compare | |||
| @merge_status, @merge_message = get_merge_message | |||
| end | |||
| private | |||
| def get_merge_message | |||
| if @base.blank? || @head.blank? | |||
| return -2, "请选择分支" | |||
| else | |||
| if @head.include?(":") | |||
| fork_project = @project.forked_projects.joins(:owner).where(users: {login: @head.to_s.split("/")[0]}).take | |||
| return -2, "请选择正确的仓库" unless fork_project.present? | |||
| @exist_pullrequest = @project.pull_requests.where(is_original: true, head: @head.to_s.split(":")[1], base: @base, status: 0, fork_project_id: fork_project.id).take | |||
| else | |||
| @exist_pullrequest = @project.pull_requests.where(is_original: false, head: @base, base: @head, status: 0).take | |||
| end | |||
| if @exist_pullrequest.present? | |||
| return -2, "在这些分支之间的合并请求已存在:<a href='/#{@owner.login}/#{@project.identifier}/pulls/#{@exist_pullrequest.id}'>#{@exist_pullrequest.try(:title)}</a>" | |||
| else | |||
| if @compare_result["Commits"].blank? && @compare_result["Diff"].blank? | |||
| return -2, "分支内容相同,无需创建合并请求" | |||
| end | |||
| end | |||
| end | |||
| return 0, "可以合并" | |||
| end | |||
| def compare | |||
| base, head = compare_params | |||
| # TODO: 处理fork的项目向源项目发送PR的base、head参数问题 | |||
| @compare_result ||= | |||
| head.include?(":") ? gitea_compare(base, head) : gitea_compare(head, base) | |||
| @head.include?(":") ? gitea_compare(@base, @head) : gitea_compare(@head, @base) | |||
| end | |||
| def compare_params | |||
| base = Addressable::URI.unescape(params[:base]) | |||
| head = params[:head].include?('json') ? params[:head]&.split('.json')[0] : params[:head] | |||
| def load_compare_params | |||
| @base = Addressable::URI.unescape(params[:base]) | |||
| @head = params[:head].include?('json') ? params[:head]&.split('.json')[0] : params[:head] | |||
| [base, head] | |||
| end | |||
| def gitea_compare(base, head) | |||
| Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head) | |||
| Gitea::Repository::Commits::CompareService.call(@owner.login, @project.identifier, base, head, current_user.gitea_token) | |||
| end | |||
| end | |||
| @@ -20,7 +20,7 @@ module ControllerRescueHandler | |||
| end | |||
| # rescue_from ActionView::MissingTemplate, with: :object_not_found | |||
| # rescue_from ActiveRecord::RecordNotFound, with: :object_not_found | |||
| rescue_from Educoder::TipException, with: :tip_show | |||
| rescue_from Gitlink::TipException, with: :tip_show | |||
| rescue_from ::ActionView::MissingTemplate, with: :missing_template | |||
| rescue_from ActiveRecord::RecordNotFound, with: :object_not_found | |||
| rescue_from ActionController::ParameterMissing, with: :render_parameter_missing | |||
| @@ -36,10 +36,10 @@ module GitCommon | |||
| begin | |||
| @commits = GitService.commits(repo_path: @repo_path) | |||
| logger.info("git first commit is #{@commits.try(:first)}") | |||
| raise Educoder::TipException.new("请先创建版本库") if @commits.nil? | |||
| raise Gitlink::TipException.new("请先创建版本库") if @commits.nil? | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| raise Educoder::TipException.new("提交记录异常") | |||
| raise Gitlink::TipException.new("提交记录异常") | |||
| end | |||
| end | |||
| @@ -34,7 +34,7 @@ module GitHelper | |||
| rescue Exception => e | |||
| Rails.logger.error(e.message) | |||
| raise Educoder::TipException.new("文档内容获取异常") | |||
| raise Gitlink::TipException.new("文档内容获取异常") | |||
| end | |||
| end | |||
| @@ -64,7 +64,7 @@ module GitHelper | |||
| # 版本库Fork功能 | |||
| def project_fork(container, original_rep_path, username) | |||
| raise Educoder::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank? | |||
| raise Gitlink::TipException.new("fork源路径为空,fork失败!") if original_rep_path.blank? | |||
| # 将要生成的仓库名字 | |||
| new_repo_name = "#{username.try(:strip)}/#{container.try(:identifier)}#{ Time.now.strftime("%Y%m%d%H%M%S")}" | |||
| # uid_logger("start fork container: repo_name is #{new_repo_name}") | |||
| @@ -41,7 +41,7 @@ module LaboratoryHelper | |||
| my_courses: "https://www.trustie.net/users/#{current_user.try(:login)}/user_courselist", | |||
| my_projects: "/users/#{current_user.try(:login)}/projects", | |||
| my_organ: "https://www.trustie.net/users/#{current_user.try(:login)}/user_organizations", | |||
| default_url: "https://www.trustie.net/", | |||
| default_url: Rails.application.config_for(:configuration)['platform_url'], | |||
| tiding_url: "https://www.trustie.net/users/#{current_user.try(:login)}/user_messages", | |||
| register_url: "https://www.trustie.net/login?login=false" | |||
| } | |||
| @@ -3,8 +3,8 @@ module RenderHelper | |||
| render json: { status: 0, message: 'success' }.merge(data) | |||
| end | |||
| def render_error(message = '') | |||
| render json: { status: -1, message: message } | |||
| def render_error(status = -1, message = '') | |||
| render json: { status: status, message: message } | |||
| end | |||
| def render_not_acceptable(message = '请求已拒绝') | |||
| @@ -29,10 +29,8 @@ class EduSettingsController < ApplicationController | |||
| respond_to do |format| | |||
| if @edu_setting.save | |||
| format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully created.' } | |||
| format.json { render :show, status: :created, location: @edu_setting } | |||
| else | |||
| format.html { render :new } | |||
| format.json { render json: @edu_setting.errors, status: :unprocessable_entity } | |||
| end | |||
| end | |||
| @@ -43,10 +41,8 @@ class EduSettingsController < ApplicationController | |||
| def update | |||
| respond_to do |format| | |||
| if @edu_setting.update(edu_setting_params) | |||
| format.html { redirect_to @edu_setting, notice: 'Edu setting was successfully updated.' } | |||
| format.json { render :show, status: :ok, location: @edu_setting } | |||
| else | |||
| format.html { render :edit } | |||
| format.json { render json: @edu_setting.errors, status: :unprocessable_entity } | |||
| end | |||
| end | |||
| @@ -57,7 +53,6 @@ class EduSettingsController < ApplicationController | |||
| def destroy | |||
| @edu_setting.destroy | |||
| respond_to do |format| | |||
| format.html { redirect_to edu_settings_url, notice: 'Edu setting was successfully destroyed.' } | |||
| format.json { head :no_content } | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class ForksController < ApplicationController | |||
| before_action :require_login | |||
| before_action :require_profile_completed, only: [:create] | |||
| before_action :load_project | |||
| before_action :authenticate_project!, :authenticate_user! | |||
| @@ -7,7 +7,7 @@ class IssueTagsController < ApplicationController | |||
| def index | |||
| issue_tags = @project.issue_tags.order("#{order_name} #{order_type}") | |||
| issue_tags = @project.issue_tags.reorder("#{order_name} #{order_type}") | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| @page = params[:page] || 1 | |||
| @limit = params[:limit] || 15 | |||
| @@ -1,19 +1,21 @@ | |||
| class IssuesController < ApplicationController | |||
| before_action :require_login, except: [:index, :show, :index_chosen] | |||
| before_action :require_profile_completed, only: [:create] | |||
| before_action :load_project | |||
| before_action :set_user | |||
| before_action :check_menu_authorize, except: [:index_chosen] | |||
| before_action :check_issue_permission | |||
| before_action :operate_issue_permission, only:[:create, :update, :destroy, :clean, :series_update, :copy] | |||
| before_action :check_project_public, only: [:index ,:show, :copy, :index_chosen, :close_issue] | |||
| before_action :set_issue, only: [:edit, :update, :destroy, :show, :copy, :close_issue, :lock_issue] | |||
| before_action :check_token_enough, only: [:create, :update] | |||
| before_action :check_token_enough, :find_atme_receivers, only: [:create, :update] | |||
| include ApplicationHelper | |||
| include TagChosenHelper | |||
| def index | |||
| return render_not_found unless @project.has_menu_permission("issues") | |||
| @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user)) | |||
| @user_admin_or_member = current_user.present? && current_user.logged? && (current_user.admin || @project.member?(current_user) || @project.is_public?) | |||
| issues = @project.issues.issue_issue.issue_index_includes | |||
| issues = issues.where(is_private: false) unless @user_admin_or_member | |||
| @@ -109,6 +111,8 @@ class IssuesController < ApplicationController | |||
| Issues::CreateForm.new({subject:issue_params[:subject]}).validate! | |||
| @issue = Issue.new(issue_params) | |||
| if @issue.save! | |||
| SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @issue&.id) if Site.has_notice_menu? | |||
| if params[:attachment_ids].present? | |||
| params[:attachment_ids].each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| @@ -138,6 +142,10 @@ class IssuesController < ApplicationController | |||
| end | |||
| @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") | |||
| Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}" | |||
| AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0 | |||
| render json: {status: 0, message: "创建成", id: @issue.id} | |||
| else | |||
| normal_status(-1, "创建失败") | |||
| @@ -156,6 +164,7 @@ class IssuesController < ApplicationController | |||
| def update | |||
| last_token = @issue.token | |||
| last_status_id = @issue.status_id | |||
| @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? | |||
| if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? | |||
| @issue&.issue_tags_relates&.destroy_all | |||
| params[:issue_tag_ids].each do |tag| | |||
| @@ -200,6 +209,26 @@ class IssuesController < ApplicationController | |||
| issue_params = issue_send_params(params).except(:issue_classify, :author_id, :project_id) | |||
| Issues::UpdateForm.new({subject:issue_params[:subject]}).validate! | |||
| if @issue.update_attributes(issue_params) | |||
| if @issue&.pull_request.present? | |||
| SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @issue&.pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @issue&.pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu? | |||
| else | |||
| previous_changes = @issue.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name) | |||
| if @issue.previous_changes[:start_date].present? | |||
| previous_changes.merge!(start_date: [@issue.previous_changes[:start_date][0].to_s, @issue.previous_changes[:start_date][1].to_s]) | |||
| end | |||
| if @issue.previous_changes[:due_date].present? | |||
| previous_changes.merge!(due_date: [@issue.previous_changes[:due_date][0].to_s, @issue.previous_changes[:due_date][1].to_s]) | |||
| end | |||
| if @issue.previous_changes[:status_id].present? && @issue.previous_changes[:status_id][1] == 5 | |||
| @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE) | |||
| end | |||
| if @issue.previous_changes[:status_id].present? && @issue.previous_changes[:status_id][0] == 5 | |||
| @issue.project_trends.where(action_type: ProjectTrend::CLOSE).destroy_all | |||
| end | |||
| SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, @issue&.id, previous_changes) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @issue&.id) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu? | |||
| end | |||
| if params[:status_id].to_i == 5 #任务由非关闭状态到关闭状态时 | |||
| @issue.issue_times.update_all(end_time: Time.now) | |||
| @issue.update_closed_issues_count_in_project! | |||
| @@ -218,7 +247,11 @@ class IssuesController < ApplicationController | |||
| change_type = change_token > 0 ? "add" : "minus" | |||
| post_to_chain(change_type, change_token.abs, current_user.try(:login)) | |||
| end | |||
| @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) | |||
| @issue.create_journal_detail(change_files, issue_files, issue_file_ids, current_user&.id) if @issue.previous_changes.present? | |||
| Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}" | |||
| AtmeService.call(current_user, @atme_receivers, @issue) if @atme_receivers.size > 0 | |||
| normal_status(0, "更新成功") | |||
| else | |||
| normal_status(-1, "更新失败") | |||
| @@ -230,7 +263,7 @@ class IssuesController < ApplicationController | |||
| end | |||
| def show | |||
| @user_permission = current_user.present? && current_user.logged? && (!@issue.is_lock || @project.member?(current_user) || current_user.admin? || @issue.user == current_user) | |||
| @user_permission = current_user.present? && current_user.logged? && (@project.member?(current_user) || current_user.admin? || @issue.user == current_user) | |||
| @issue_attachments = @issue.attachments | |||
| @issue_user = @issue.user | |||
| @issue_assign_to = @issue.get_assign_user | |||
| @@ -251,6 +284,7 @@ class IssuesController < ApplicationController | |||
| status_id = @issue.status_id | |||
| token = @issue.token | |||
| login = @issue.user.try(:login) | |||
| SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, @issue&.subject, @issue.assigned_to_id, @issue.author_id) if Site.has_notice_menu? | |||
| if @issue.destroy | |||
| if issue_type == "2" && status_id != 5 | |||
| post_to_chain("add", token, login) | |||
| @@ -270,8 +304,12 @@ class IssuesController < ApplicationController | |||
| def clean | |||
| #批量删除,暂时只能删除未悬赏的 | |||
| issue_ids = params[:ids] | |||
| if issue_ids.present? | |||
| if Issue.where(id: issue_ids, issue_type: "1").destroy_all | |||
| issues = Issue.where(id: issue_ids, issue_type: "1") | |||
| if issues.present? | |||
| issues.find_each do |i| | |||
| SendTemplateMessageJob.perform_later('IssueDeleted', current_user.id, i&.subject, i.assigned_to_id, i.author_id) if Site.has_notice_menu? | |||
| end | |||
| if issues.destroy_all | |||
| normal_status(0, "删除成功") | |||
| else | |||
| normal_status(-1, "删除失败") | |||
| @@ -301,9 +339,28 @@ class IssuesController < ApplicationController | |||
| # update_hash = params[:issue] | |||
| issue_ids = params[:ids] | |||
| if issue_ids.present? | |||
| issues = Issue.where(id: issue_ids) | |||
| if update_hash.blank? | |||
| normal_status(-1, "请选择批量更新内容") | |||
| elsif Issue.where(id: issue_ids).update_all(update_hash) | |||
| elsif issues&.update(update_hash) | |||
| issues.each do |i| | |||
| i.create_journal_detail(false, [], [], current_user&.id) if i.previous_changes.present? | |||
| previous_changes = i.previous_changes.slice(:status_id, :assigned_to_id, :tracker_id, :priority_id, :fixed_version_id, :done_ratio, :issue_tags_value, :branch_name) | |||
| if i.previous_changes[:start_date].present? | |||
| previous_changes.merge!(start_date: [i.previous_changes[:start_date][0].to_s, i.previous_changes[:start_date][1].to_s]) | |||
| end | |||
| if i.previous_changes[:due_date].present? | |||
| previous_changes.merge!(due_date: [i.previous_changes[:due_date][0].to_s, i.previous_changes[:due_date][1].to_s]) | |||
| end | |||
| if i.previous_changes[:status_id].present? && i.previous_changes[:status_id][1] == 5 | |||
| i.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: ProjectTrend::CLOSE) | |||
| end | |||
| if i.previous_changes[:status_id].present? && i.previous_changes[:status_id][0] == 5 | |||
| i.project_trends.where(action_type: ProjectTrend::CLOSE).destroy_all | |||
| end | |||
| SendTemplateMessageJob.perform_later('IssueChanged', current_user.id, i&.id, previous_changes) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, i&.id) if i.previous_changes[:assigned_to_id].present? && Site.has_notice_menu? | |||
| end | |||
| normal_status(0, "批量更新成功") | |||
| else | |||
| normal_status(-1, "批量更新失败") | |||
| @@ -315,7 +372,10 @@ class IssuesController < ApplicationController | |||
| def copy | |||
| @new_issue = @issue.dup | |||
| @new_issue.author_id = current_user.id | |||
| if @new_issue.save | |||
| SendTemplateMessageJob.perform_later('IssueAssigned', current_user.id, @new_issue&.id) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('ProjectIssue', current_user.id, @new_issue&.id) if Site.has_notice_menu? | |||
| issue_tags = @issue.issue_tags.pluck(:id) | |||
| if issue_tags.present? | |||
| issue_tags.each do |tag| | |||
| @@ -393,25 +453,29 @@ class IssuesController < ApplicationController | |||
| def check_project_public | |||
| unless @project.is_public || @project.member?(current_user) || current_user.admin? || (@project.user_id == current_user.id) | |||
| normal_status(-1, "您没有权限") | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def set_issue | |||
| @issue = Issue.find_by_id(params[:id]) | |||
| if @issue.blank? | |||
| normal_status(-1, "标签不存在") | |||
| elsif @issue.is_lock &&!(@project.member?(current_user) || current_user.admin?) | |||
| normal_status(-1, "您没有权限") | |||
| return render_not_found | |||
| elsif !(@project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id)))) | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def check_issue_permission | |||
| unless @project.is_public || (current_user.present? && (@project.member?(current_user) || current_user&.admin? || (@project.user_id == current_user&.id))) | |||
| normal_status(-1, "您没有权限") | |||
| return render_forbidden | |||
| end | |||
| end | |||
| def operate_issue_permission | |||
| return render_forbidden("您没有权限进行此操作.") unless current_user.present? && current_user.logged? && (current_user.admin? || @project.member?(current_user) || @project.is_public?) | |||
| end | |||
| def export_issues(issues) | |||
| @table_columns = %w(ID 类型 标题 描述 状态 指派给 优先级 标签 发布人 创建时间 里程碑 开始时间 截止时间 完成度 分类 金额 属于) | |||
| @export_issues = [] | |||
| @@ -491,4 +555,8 @@ class IssuesController < ApplicationController | |||
| return normal_status(-1, "您的token值不足") if JSON.parse(response.body)["balance"].to_i < params[:token].to_i | |||
| end | |||
| end | |||
| def check_menu_authorize | |||
| return render_not_found unless @project.has_menu_permission("issues") | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class JournalsController < ApplicationController | |||
| before_action :require_login, except: [:index, :get_children_journals] | |||
| before_action :require_profile_completed, :find_atme_receivers, only: [:create] | |||
| before_action :set_issue | |||
| before_action :check_issue_permission | |||
| before_action :set_journal, only: [:destroy, :edit, :update] | |||
| @@ -21,32 +22,35 @@ class JournalsController < ApplicationController | |||
| if notes.blank? | |||
| normal_status(-1, "评论内容不能为空") | |||
| else | |||
| journal_params = { | |||
| journalized_id: @issue.id , | |||
| journalized_type: "Issue", | |||
| user_id: current_user.id , | |||
| notes: notes.to_s.strip, | |||
| parent_id: params[:parent_id] | |||
| } | |||
| journal = Journal.new journal_params | |||
| if journal.save | |||
| if params[:attachment_ids].present? | |||
| params[:attachment_ids].each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| unless attachment.blank? | |||
| attachment.container = journal | |||
| attachment.author_id = current_user.id | |||
| attachment.description = "" | |||
| attachment.save | |||
| ActiveRecord::Base.transaction do | |||
| journal_params = { | |||
| journalized_id: @issue.id , | |||
| journalized_type: "Issue", | |||
| user_id: current_user.id , | |||
| notes: notes.to_s.strip, | |||
| parent_id: params[:parent_id] | |||
| } | |||
| journal = Journal.new journal_params | |||
| if journal.save | |||
| if params[:attachment_ids].present? | |||
| params[:attachment_ids].each do |id| | |||
| attachment = Attachment.select(:id, :container_id, :container_type)&.find_by_id(id) | |||
| unless attachment.blank? | |||
| attachment.container = journal | |||
| attachment.author_id = current_user.id | |||
| attachment.description = "" | |||
| attachment.save | |||
| end | |||
| end | |||
| end | |||
| Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}" | |||
| AtmeService.call(current_user, @atme_receivers, journal) if @atme_receivers.size > 0 | |||
| # @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal") | |||
| render :json => { status: 0, message: "评论成功", id: journal.id} | |||
| # normal_status(0, "评论成功") | |||
| else | |||
| normal_status(-1, "评论失败") | |||
| end | |||
| # @issue.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "journal") | |||
| render :json => { status: 0, message: "评论成功", id: journal.id} | |||
| # normal_status(0, "评论成功") | |||
| else | |||
| normal_status(-1, "评论失败") | |||
| end | |||
| end | |||
| end | |||
| @@ -23,9 +23,9 @@ class MainController < ApplicationController | |||
| # TODO: 这块之后需要整合,者架构重新变化,统一跳转到index后再路由分发 | |||
| if params[:path] && params[:path]&.include?("h5educoderbuild") && params[:path].split("/").first == "h5educoderbuild" | |||
| render file: 'public/h5educoderbuild/index.html', :layout => false | |||
| render file: 'public/h5educoderbuild/index.html', :layout => false, :content_type=> 'text/html' | |||
| else | |||
| render file: 'public/react/build/index.html', :layout => false | |||
| render file: 'public/react/build/index.html', :layout => false, :content_type=> 'text/html' | |||
| end | |||
| end | |||
| @@ -2,12 +2,15 @@ class MembersController < ApplicationController | |||
| before_action :require_login | |||
| before_action :load_project | |||
| before_action :find_user_with_id, only: %i[create remove change_role] | |||
| before_action :check_user_profile_completed, only: [:create] | |||
| before_action :operate!, except: %i[index] | |||
| before_action :check_member_exists!, only: %i[create] | |||
| before_action :check_member_not_exists!, only: %i[remove change_role] | |||
| def create | |||
| interactor = Projects::AddMemberInteractor.call(@project.owner, @project, @user) | |||
| SendTemplateMessageJob.perform_later('ProjectJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('ProjectMemberJoined', current_user.id, @user.id, @project.id) if Site.has_notice_menu? | |||
| render_response(interactor) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| @@ -27,6 +30,8 @@ class MembersController < ApplicationController | |||
| def remove | |||
| interactor = Projects::DeleteMemberInteractor.call(@project.owner, @project, @user) | |||
| SendTemplateMessageJob.perform_later('ProjectLeft', current_user.id, @user.id, @project.id) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('ProjectMemberLeft', current_user.id, @user.id, @project.id) if Site.has_notice_menu? | |||
| render_response(interactor) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| @@ -35,6 +40,7 @@ class MembersController < ApplicationController | |||
| def change_role | |||
| interactor = Projects::ChangeMemberRoleInteractor.call(@project.owner, @project, @user, params[:role]) | |||
| SendTemplateMessageJob.perform_later('ProjectRole', current_user.id, @user.id, @project.id, message_role_name) if Site.has_notice_menu? | |||
| render_response(interactor) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| @@ -47,7 +53,7 @@ class MembersController < ApplicationController | |||
| end | |||
| def member_exists? | |||
| @project.members.exists?(params[:user_id]) | |||
| @project.members.exists?(user_id: params[:user_id]) | |||
| end | |||
| def operate! | |||
| @@ -55,10 +61,24 @@ class MembersController < ApplicationController | |||
| end | |||
| def check_member_exists! | |||
| return render_result(1, "user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists? | |||
| return render_error("user_id为#{params[:user_id]}的用户已经是项目成员") if member_exists? | |||
| end | |||
| def check_member_not_exists! | |||
| return render_result(1, "user_id为#{params[:user_id]}的用户还不是项目成员") unless @project.member?(params[:user_id]) | |||
| return render_error("user_id为#{params[:user_id]}的用户还不是项目成员") unless member_exists? | |||
| end | |||
| def check_user_profile_completed | |||
| require_user_profile_completed(@user) | |||
| end | |||
| def message_role_name | |||
| case params[:role] | |||
| when 'Manager' then '管理员' | |||
| when 'Developer' then '开发者' | |||
| when 'Reporter' then '报告者' | |||
| else | |||
| '' | |||
| end | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class Organizations::OrganizationsController < Organizations::BaseController | |||
| before_action :require_login, except: [:index, :show, :recommend] | |||
| before_action :require_profile_completed, only: [:create] | |||
| before_action :convert_image!, only: [:create, :update] | |||
| before_action :load_organization, only: [:show, :update, :destroy] | |||
| before_action :check_user_can_edit_org, only: [:update, :destroy] | |||
| @@ -21,10 +22,12 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| @can_create_project = @organization.can_create_project?(current_user.id) | |||
| @is_admin = can_edit_org? | |||
| @is_member = @organization.is_member?(current_user.id) | |||
| Cache::V2::OwnerCommonService.new(@organization.id).read | |||
| end | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| tip_exception("无法使用以下关键词:#{organization_params[:name]},请重新命名") if ReversedKeyword.check_exists?(organization_params[:name]) | |||
| Organizations::CreateForm.new(organization_params).validate! | |||
| @organization = Organizations::CreateService.call(current_user, organization_params) | |||
| Util.write_file(@image, avatar_path(@organization)) if params[:image].present? | |||
| @@ -41,7 +44,8 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| @organization.login = organization_params[:name] if organization_params[:name].present? | |||
| @organization.nickname = organization_params[:nickname] if organization_params[:nickname].present? | |||
| @organization.save! | |||
| @organization.organization_extension.update_attributes!(organization_params.except(:name, :nickname)) | |||
| sync_organization_extension! | |||
| Gitea::Organization::UpdateService.call(@organization.gitea_token, login, @organization.reload) | |||
| Util.write_file(@image, avatar_path(@organization)) if params[:image].present? | |||
| end | |||
| @@ -65,8 +69,7 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| def recommend | |||
| recommend = %W(xuos Huawei_Technology openatom_foundation pkecosystem TensorLayer) | |||
| @organizations = Organization.with_visibility(%w(common)) | |||
| .where(login: recommend).select(:id, :login, :firstname, :lastname, :nickname) | |||
| @organizations = Organization.includes(:organization_extension).where(organization_extensions: {recommend: true}).to_a.each_slice(group_size).to_a | |||
| end | |||
| private | |||
| @@ -77,6 +80,10 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| :max_repo_creation, :nickname) | |||
| end | |||
| def group_size | |||
| params.fetch(:group_size, 4).to_i | |||
| end | |||
| def password | |||
| params.fetch(:password, "") | |||
| end | |||
| @@ -95,4 +102,18 @@ class Organizations::OrganizationsController < Organizations::BaseController | |||
| %w(desc asc).include?(params[:sort_direction]) ? params[:sort_direction] : 'desc' | |||
| end | |||
| def set_max_repo_creation | |||
| organization_params[:max_repo_creation].blank? ? -1 : organization_params[:max_repo_creation] | |||
| end | |||
| def organization_extension_params | |||
| organization_params | |||
| .except(:name, :nickname) | |||
| .merge(max_repo_creation: set_max_repo_creation) | |||
| end | |||
| def sync_organization_extension! | |||
| @organization.organization_extension.update_attributes!(organization_extension_params) | |||
| end | |||
| end | |||
| @@ -1,6 +1,7 @@ | |||
| class Organizations::TeamUsersController < Organizations::BaseController | |||
| before_action :load_organization, :load_team | |||
| before_action :load_operate_user, only: [:create, :destroy] | |||
| before_action :check_user_profile_completed, only: [:create] | |||
| before_action :load_team_user, only: [:destroy] | |||
| before_action :check_user_can_edit_org, only: [:create, :destroy] | |||
| @@ -17,6 +18,7 @@ class Organizations::TeamUsersController < Organizations::BaseController | |||
| ActiveRecord::Base.transaction do | |||
| @team_user = TeamUser.build(@organization.id, @operate_user.id, @team.id) | |||
| @organization_user = OrganizationUser.build(@organization.id, @operate_user.id) | |||
| SendTemplateMessageJob.perform_later('OrganizationRole', @operate_user.id, @organization.id, @team.authorize_name) if Site.has_notice_menu? | |||
| Gitea::Organization::TeamUser::CreateService.call(@organization.gitea_token, @team.gtid, @operate_user.login) | |||
| end | |||
| rescue Exception => e | |||
| @@ -29,6 +31,11 @@ class Organizations::TeamUsersController < Organizations::BaseController | |||
| ActiveRecord::Base.transaction do | |||
| @team_user.destroy! | |||
| Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, @operate_user.login) | |||
| org_team_users = @organization.team_users.where(user_id: @operate_user.id) | |||
| unless org_team_users.present? | |||
| @organization.organization_users.find_by(user_id: @operate_user.id).destroy! | |||
| Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, @operate_user.login) | |||
| end | |||
| render_ok | |||
| end | |||
| rescue Exception => e | |||
| @@ -43,6 +50,11 @@ class Organizations::TeamUsersController < Organizations::BaseController | |||
| ActiveRecord::Base.transaction do | |||
| @team_user.destroy! | |||
| Gitea::Organization::TeamUser::DeleteService.call(@organization.gitea_token, @team.gtid, current_user.login) | |||
| org_team_users = @organization.team_users.where(user_id: current_user.id) | |||
| unless org_team_users.present? | |||
| @organization.organization_users.find_by(user_id: current_user.id).destroy! | |||
| Gitea::Organization::OrganizationUser::DeleteService.call(@organization.gitea_token, @organization.login, current_user.login) | |||
| end | |||
| render_ok | |||
| end | |||
| rescue Exception => e | |||
| @@ -73,4 +85,8 @@ class Organizations::TeamUsersController < Organizations::BaseController | |||
| tip_exception("组织团队成员不存在") if @team_user.nil? | |||
| end | |||
| def check_user_profile_completed | |||
| require_user_profile_completed(@operate_user) | |||
| end | |||
| end | |||
| @@ -1,5 +1,5 @@ | |||
| class OwnersController < ApplicationController | |||
| before_action :require_login | |||
| before_action :require_login, only: [:index] | |||
| def index | |||
| @owners = [] | |||
| @@ -9,4 +9,53 @@ class OwnersController < ApplicationController | |||
| teams: {can_create_org_project: true}) | |||
| .distinct | |||
| end | |||
| end | |||
| def show | |||
| @owner = Owner.find_by(login: params[:id]) || Owner.find_by(id: params[:id]) | |||
| return render_not_found unless @owner.present? | |||
| # 组织 | |||
| if @owner.is_a?(Organization) | |||
| return render_forbidden("没有查看组织的权限") if org_limited_condition || org_privacy_condition | |||
| @can_create_project = @owner.can_create_project?(current_user.id) | |||
| @is_admin = current_user.admin? || @owner.is_owner?(current_user.id) | |||
| @is_member = @owner.is_member?(current_user.id) | |||
| # 用户 | |||
| else | |||
| #待办事项,现在未做 | |||
| if User.current.admin? || User.current.login == @owner.login | |||
| @waiting_applied_messages = @owner.applied_messages.waiting | |||
| @common_applied_transfer_projects = AppliedTransferProject.where(owner_id: @owner.id).common + AppliedTransferProject.where(owner_id: Organization.joins(team_users: :team).where(team_users: {user_id: @owner.id}, teams: {authorize: %w(admin owner)} )).common | |||
| @common_applied_projects = AppliedProject.where(project_id: @owner.full_admin_projects).common | |||
| @undo_events = @waiting_applied_messages.size + @common_applied_transfer_projects.size + @common_applied_projects.size | |||
| else | |||
| @waiting_applied_messages = AppliedMessage.none | |||
| @common_applied_transfer_projects = AppliedTransferProject.none | |||
| @common_applied_projects = AppliedProject.none | |||
| @undo_events = 0 | |||
| end | |||
| #用户的组织数量 | |||
| # @user_composes_count = @user.composes.size | |||
| @user_composes_count = 0 | |||
| user_organizations = User.current.logged? ? @owner.organizations.with_visibility(%w(common limited)) + @owner.organizations.with_visibility("privacy").joins(:team_users).where(team_users: {user_id: current_user.id}) : @owner.organizations.with_visibility("common") | |||
| @user_org_count = user_organizations.size | |||
| normal_projects = Project.members_projects(@owner.id).to_sql | |||
| org_projects = Project.joins(team_projects: [team: :team_users]).where(team_users: {user_id: @owner.id}).to_sql | |||
| projects = Project.from("( #{ normal_projects} UNION #{ org_projects } ) AS projects").distinct | |||
| user_projects = User.current.logged? && (User.current.admin? || User.current.login == @owner.login) ? projects : projects.visible | |||
| @projects_common_count = user_projects.common.size | |||
| @projects_mirrior_count = user_projects.mirror.size | |||
| @projects_sync_mirrior_count = user_projects.sync_mirror.size | |||
| puts @owner.as_json | |||
| end | |||
| end | |||
| private | |||
| def org_limited_condition | |||
| @owner.organization_extension.limited? && !current_user.logged? | |||
| end | |||
| def org_privacy_condition | |||
| return false if current_user.admin? | |||
| @owner.organization_extension.privacy? && @owner.organization_users.where(user_id: current_user.id).blank? | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class PraiseTreadController < ApplicationController | |||
| before_action :require_login, except: %i[index] | |||
| before_action :require_profile_completed, only: [:like] | |||
| before_action :find_project_with_id | |||
| def index | |||
| @@ -5,6 +5,10 @@ class ProjectCategoriesController < ApplicationController | |||
| @project_categories = q.result(distinct: true) | |||
| end | |||
| def pinned_index | |||
| @project_categories = ProjectCategory.where.not(pinned_index: 0).order(pinned_index: :desc) | |||
| end | |||
| def group_list | |||
| @project_categories = ProjectCategory.where('projects_count > 0').order(projects_count: :desc) | |||
| # projects = Project.no_anomory_projects.visible | |||
| @@ -0,0 +1,26 @@ | |||
| class ProjectRankController < ApplicationController | |||
| # 根据时间获取热门项目 | |||
| def index | |||
| $redis_cache.zunionstore("recent-days-project-rank", get_timeable_key_names) | |||
| deleted_data = $redis_cache.smembers("v2-project-rank-deleted") | |||
| $redis_cache.zrem("recent-days-project-rank", deleted_data) unless deleted_data.blank? | |||
| @project_rank = $redis_cache.zrevrange("recent-days-project-rank", 0, 4, withscores: true) | |||
| rescue Exception => e | |||
| @project_rank = [] | |||
| end | |||
| private | |||
| # 默认显示7天的 | |||
| def time | |||
| params.fetch(:time, 7).to_i | |||
| end | |||
| def get_timeable_key_names | |||
| names_array = [] | |||
| (0...time).to_a.each do |i| | |||
| date_time_string = (Date.today - i.days).to_s | |||
| names_array << "v2-project-rank-#{date_time_string}" | |||
| end | |||
| names_array | |||
| end | |||
| end | |||
| @@ -3,7 +3,7 @@ class ProjectTrendsController < ApplicationController | |||
| before_action :check_project_public | |||
| def index | |||
| project_trends = @project.project_trends.includes(:user, trend: :user) | |||
| project_trends = @project.project_trends.preload(:user, trend: :user) | |||
| check_time = params[:time] #时间的筛选 | |||
| check_type = params[:type] #动态类型的筛选,目前已知的有 Issue, PullRequest, Version | |||
| @@ -14,20 +14,25 @@ class ProjectTrendsController < ApplicationController | |||
| project_trends = project_trends.where("created_at between ? and ?",(Time.now.beginning_of_day - check_time.days), Time.now.end_of_day) | |||
| end | |||
| @project_open_issues_count = project_trends.where(trend_type: "Issue", action_type: "create").size | |||
| @project_close_issues_count = project_trends.where(trend_type: "Issue", action_type: "close").size | |||
| @project_issues_count = @project_open_issues_count + @project_close_issues_count | |||
| @project_pr_count = project_trends.where(trend_type: "PullRequest", action_type: "close").size | |||
| @project_new_pr_count = project_trends.where(trend_type: "PullRequest", action_type: "create").size | |||
| @project_pr_all_count = @project_pr_count + @project_new_pr_count | |||
| @project_issues_count = project_trends.where(trend_type: "Issue", action_type: "create").size | |||
| @project_open_issues_count = @project_issues_count - @project_close_issues_count | |||
| @project_pr_count = project_trends.where(trend_type: "PullRequest", action_type: ["close", "merge"]).size | |||
| @project_pr_all_count = project_trends.where(trend_type: "PullRequest", action_type: "create").size | |||
| @project_new_pr_count = @project_pr_all_count - @project_pr_count | |||
| if check_type.present? | |||
| project_trends = project_trends.where(trend_type: check_type.to_s.strip) | |||
| end | |||
| if check_status.present? | |||
| project_trends = project_trends.where(action_type: check_status.to_s.strip) | |||
| if check_status == "delay" || check_status == "close" | |||
| project_trends = project_trends.where(action_type: ["close", "merge"]) | |||
| else | |||
| project_trends = project_trends.where(action_type: ["create"]).where.not(trend_id: project_trends.where(action_type: ["close", "merge"]).pluck(:trend_id)) | |||
| end | |||
| else | |||
| project_trends = project_trends.where(action_type: "create") | |||
| end | |||
| project_trends = project_trends.order("created_at desc") | |||
| @@ -1,5 +1,6 @@ | |||
| class Projects::AppliedTransferProjectsController < Projects::BaseController | |||
| before_action :check_auth | |||
| before_action :check_user_profile_completed, only: [:create] | |||
| def organizations | |||
| @organizations = Organization.includes(:organization_extension).joins(team_users: :team).where(team_users: {user_id: current_user.id}, teams: {authorize: %w(admin owner)}) | |||
| @@ -23,4 +24,10 @@ class Projects::AppliedTransferProjectsController < Projects::BaseController | |||
| def check_auth | |||
| return render_forbidden unless current_user.admin? ||@project.owner?(current_user) | |||
| end | |||
| def check_user_profile_completed | |||
| @owner = Owner.find_by(login: params[:owner_name]) | |||
| return if @owner.is_a?(Organization) | |||
| require_user_profile_completed(@owner) | |||
| end | |||
| end | |||
| @@ -4,4 +4,7 @@ class Projects::BaseController < ApplicationController | |||
| before_action :load_project | |||
| before_action :load_repository | |||
| def require_manager! | |||
| return render_forbidden('你没有权限操作') unless current_user.admin? || @project.manager?(current_user) | |||
| end | |||
| end | |||
| @@ -0,0 +1,6 @@ | |||
| class Projects::MembersController < Projects::BaseController | |||
| def index | |||
| users = @project.all_collaborators.like(params[:search]).includes(:user_extension) | |||
| @users = kaminari_paginate(users) | |||
| end | |||
| end | |||
| @@ -1,4 +1,5 @@ | |||
| class Projects::ProjectAppliesController < Projects::BaseController | |||
| before_action :require_profile_completed, only: [:create] | |||
| def create | |||
| project = Projects::ApplyJoinService.call(current_user, create_params) | |||
| render_ok(project_id: project.id) | |||
| @@ -6,7 +6,8 @@ class Projects::ProjectUnitsController < Projects::BaseController | |||
| def create | |||
| if current_user.admin? || @project.manager?(current_user) | |||
| ActiveRecord::Base.transaction do | |||
| ProjectUnit.update_by_unit_types!(@project, unit_types) | |||
| before_units, after_units = ProjectUnit.update_by_unit_types!(@project, unit_types) | |||
| SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, {navbar: true}) unless before_units.eql?(after_units) if Site.has_notice_menu? | |||
| render_ok | |||
| end | |||
| else | |||
| @@ -14,7 +14,7 @@ class Projects::TeamsController < Projects::BaseController | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| @team_project = TeamProject.build(@owner.id, @operate_team.id, @project.id) | |||
| Gitea::Organization::TeamProject::CreateService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) | |||
| Gitea::Organization::TeamProject::CreateService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) | |||
| render_ok | |||
| end | |||
| rescue Exception => e | |||
| @@ -25,7 +25,7 @@ class Projects::TeamsController < Projects::BaseController | |||
| def destroy | |||
| ActiveRecord::Base.transaction do | |||
| @team_project.destroy! | |||
| Gitea::Organization::TeamProject::DeleteService.call(@owner.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) | |||
| Gitea::Organization::TeamProject::DeleteService.call(current_user.gitea_token, @operate_team.gtid, @owner.login, @project.identifier) | |||
| render_ok | |||
| end | |||
| rescue Exception => e | |||
| @@ -0,0 +1,116 @@ | |||
| class Projects::WebhooksController < Projects::BaseController | |||
| before_action :require_manager! | |||
| before_action :find_webhook, only:[:edit, :update, :destroy, :tasks, :test] | |||
| def index | |||
| @webhooks = @project.webhooks | |||
| @webhooks = kaminari_paginate(@webhooks) | |||
| end | |||
| def create | |||
| ActiveRecord::Base.transaction do | |||
| return render_error("webhooks数量已到上限!请删除暂不使用的webhooks以进行添加操作") if @project.webhooks.size > 19 | |||
| return render_error("参数错误.") unless webhook_params.present? | |||
| form = Projects::Webhooks::CreateForm.new(webhook_params) | |||
| return render json: {status: -1, message: form.errors} unless form.validate! | |||
| response = Gitea::Repository::Webhooks::CreateService.new(operating_token, @project&.owner&.login, @project&.identifier, gitea_webhooks_params).call | |||
| if response[0] == 201 | |||
| @webhook = response[2] | |||
| else | |||
| render_error("创建失败.") | |||
| end | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def edit | |||
| end | |||
| def update | |||
| return render_error("参数错误.") unless webhook_params.present? | |||
| form = Projects::Webhooks::CreateForm.new(webhook_params) | |||
| return render json: {status: -1, message: form.errors} unless form.validate! | |||
| response = Gitea::Repository::Webhooks::UpdateService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id, gitea_webhooks_params) | |||
| if response[0] == 200 | |||
| @webhook = response[2] | |||
| render_ok | |||
| else | |||
| render_error("更新失败.") | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def destroy | |||
| response = Gitea::Repository::Webhooks::DeleteService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id) | |||
| if response[0] == 204 | |||
| @webhook = response[2] | |||
| render_ok | |||
| else | |||
| render_error("删除失败.") | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def tasks | |||
| @tasks = @webhook.tasks.where(is_delivered: true).order("delivered desc") | |||
| @tasks = kaminari_paginate(@tasks) | |||
| end | |||
| def test | |||
| ActiveRecord::Base.transaction do | |||
| response = Gitea::Repository::Webhooks::TestService.call(operating_token, @project&.owner&.login, @project&.identifier, @webhook.id) | |||
| if response[0] == 204 | |||
| render_ok | |||
| else | |||
| render_error("测试推送失败.") | |||
| end | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| private | |||
| def find_webhook | |||
| @webhook = @project.webhooks.find_by_id(params[:id]) | |||
| return render_not_found if @webhook.nil? | |||
| end | |||
| def webhook_params | |||
| params.require(:webhook).permit(:url, :type, :http_method, :content_type, :secret, :active, :branch_filter, events: []) | |||
| end | |||
| def webhook_type | |||
| webhook_params.fetch(:type, "gitea") | |||
| end | |||
| def webhook_branch_filter | |||
| webhook_params.fetch(:branch_filter, "*") | |||
| end | |||
| def gitea_webhooks_params | |||
| { | |||
| active: webhook_params[:active], | |||
| branch_filter: webhook_branch_filter, | |||
| config: { | |||
| content_type: webhook_params[:content_type], | |||
| url: webhook_params[:url], | |||
| http_method: webhook_params[:http_method], | |||
| secret: webhook_params[:secret] | |||
| }, | |||
| events: webhook_params[:events], | |||
| type: webhook_type, | |||
| } | |||
| end | |||
| def operating_token | |||
| @project.member?(current_user) ? current_user.gitea_token : @project&.owner&.gitea_token | |||
| end | |||
| end | |||
| @@ -4,8 +4,9 @@ class ProjectsController < ApplicationController | |||
| include ProjectsHelper | |||
| include Acceleratorable | |||
| before_action :require_login, except: %i[index branches group_type_list simple show fork_users praise_users watch_users recommend about menu_list] | |||
| before_action :load_project, except: %i[index group_type_list migrate create recommend] | |||
| before_action :require_login, except: %i[index branches branches_slice group_type_list simple show fork_users praise_users watch_users recommend banner_recommend about menu_list] | |||
| before_action :require_profile_completed, only: [:create, :migrate] | |||
| before_action :load_repository, except: %i[index group_type_list migrate create recommend banner_recommend] | |||
| before_action :authorizate_user_can_edit_project!, only: %i[update] | |||
| before_action :project_public?, only: %i[fork_users praise_users watch_users] | |||
| @@ -16,20 +17,21 @@ class ProjectsController < ApplicationController | |||
| menu.append(menu_hash_by_name("code")) if @project.has_menu_permission("code") | |||
| menu.append(menu_hash_by_name("issues")) if @project.has_menu_permission("issues") | |||
| menu.append(menu_hash_by_name("pulls")) if @project.has_menu_permission("pulls") | |||
| menu.append(menu_hash_by_name("wiki")) if @project.has_menu_permission("wiki") | |||
| menu.append(menu_hash_by_name("devops")) if @project.has_menu_permission("devops") | |||
| menu.append(menu_hash_by_name("versions")) if @project.has_menu_permission("versions") | |||
| menu.append(menu_hash_by_name("resources")) if @project.has_menu_permission("resources") | |||
| menu.append(menu_hash_by_name("activity")) | |||
| menu.append(menu_hash_by_name("setting")) if current_user.admin? || @project.manager?(current_user) | |||
| menu.append(menu_hash_by_name("settings")) if current_user.admin? || @project.manager?(current_user) | |||
| render json: menu | |||
| end | |||
| def index | |||
| scope = Projects::ListQuery.call(params) | |||
| scope = current_user.logged? ? Projects::ListQuery.call(params, current_user.id) : Projects::ListQuery.call(params) | |||
| # @projects = kaminari_paginate(scope) | |||
| @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) | |||
| @projects = kaminari_paginate(scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units)) | |||
| # @projects = paginate scope.includes(:project_category, :project_language, :repository, :project_educoder, :owner, :project_units) | |||
| category_id = params[:category_id] | |||
| @total_count = | |||
| @@ -84,6 +86,13 @@ class ProjectsController < ApplicationController | |||
| @branches = result.is_a?(Hash) && result.key?(:status) ? [] : result | |||
| end | |||
| def branches_slice | |||
| return @branches = [] unless @project.forge? | |||
| slice_result = Gitea::Repository::Branches::ListSliceService.call(@owner, @project.identifier) | |||
| @branches_slice = slice_result.is_a?(Hash) && slice_result.key?(:status) ? [] : slice_result | |||
| end | |||
| def group_type_list | |||
| project_statics = ProjectStatistic.first | |||
| @@ -108,28 +117,35 @@ class ProjectsController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| # TODO: | |||
| # 临时特殊处理修改website、lesson_url操作方法 | |||
| if project_params.has_key?("website") | |||
| if project_params.has_key?("website") | |||
| @project.update(project_params) | |||
| elsif project_params.has_key?("default_branch") | |||
| @project.update(project_params) | |||
| gitea_params = { | |||
| default_branch: @project.default_branch | |||
| } | |||
| Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) | |||
| else | |||
| validate_params = project_params.slice(:name, :description, | |||
| :project_category_id, :project_language_id, :private) | |||
| :project_category_id, :project_language_id, :private, :identifier) | |||
| Projects::UpdateForm.new(validate_params).validate! | |||
| Projects::UpdateForm.new(validate_params.merge(user_id: @project.user_id, project_identifier: @project.identifier)).validate! | |||
| private = params[:private] || false | |||
| private = @project.forked_from_project.present? ? !@project.forked_from_project.is_public : params[:private] || false | |||
| new_project_params = project_params.except(:private).merge(is_public: !private) | |||
| @project.update_attributes!(new_project_params) | |||
| @project.forked_projects.update_all(is_public: @project.is_public) | |||
| gitea_params = { | |||
| private: private, | |||
| default_branch: @project.default_branch, | |||
| website: @project.website | |||
| website: @project.website, | |||
| name: @project.identifier | |||
| } | |||
| if [true, false].include? private | |||
| Gitea::Repository::UpdateService.call(@owner, @project.identifier, gitea_params) | |||
| @project.repository.update_column(:hidden, private) | |||
| end | |||
| gitea_repo = Gitea::Repository::UpdateService.call(@owner, @project&.repository&.identifier, gitea_params) | |||
| @project.repository.update_attributes({hidden: gitea_repo["private"], identifier: gitea_repo["name"]}) | |||
| end | |||
| SendTemplateMessageJob.perform_later('ProjectSettingChanged', current_user.id, @project&.id, @project.previous_changes.slice(:name, :description, :project_category_id, :project_language_id, :is_public, :identifier)) if Site.has_notice_menu? | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| @@ -144,6 +160,7 @@ class ProjectsController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| Gitea::Repository::DeleteService.new(@project.owner, @project.identifier).call | |||
| @project.destroy! | |||
| @project.forked_projects.update_all(forked_from_project_id: nil) | |||
| render_ok | |||
| end | |||
| else | |||
| @@ -173,6 +190,8 @@ class ProjectsController < ApplicationController | |||
| end | |||
| def simple | |||
| # 为了缓存活跃项目的基本信息,后续删除 | |||
| Cache::V2::ProjectCommonService.new(@project.id).read | |||
| json_response(@project, current_user) | |||
| end | |||
| @@ -180,6 +199,10 @@ class ProjectsController < ApplicationController | |||
| @projects = Project.recommend.includes(:repository, :project_category, :owner).order(visits: :desc) | |||
| end | |||
| def banner_recommend | |||
| @projects = Project.recommend.where.not(recommend_index: 0).includes(:project_category, :owner, :project_language).order(recommend_index: :desc) | |||
| end | |||
| def about | |||
| @project_detail = @project.project_detail | |||
| @attachments = Array(@project_detail&.attachments) if request.get? | |||
| @@ -211,7 +234,7 @@ class ProjectsController < ApplicationController | |||
| private | |||
| def project_params | |||
| params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, | |||
| params.permit(:user_id, :name, :description, :repository_name, :website, :lesson_url, :default_branch, :identifier, | |||
| :project_category_id, :project_language_id, :license_id, :ignore_id, :private) | |||
| end | |||
| @@ -0,0 +1,64 @@ | |||
| class PublicKeysController < ApplicationController | |||
| before_action :require_login | |||
| before_action :find_public_key, only: [:destroy] | |||
| def index | |||
| @public_keys = current_user.public_keys | |||
| @public_keys = kaminari_paginate(@public_keys) | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def create | |||
| return render_error("参数错误") if public_key_params.blank? | |||
| return render_ok({status: 10002, message: "请输入密钥"}) if public_key_params[:key].blank? | |||
| return render_ok({status: 10001, message: "请输入标题"}) if public_key_params[:title].blank? | |||
| @gitea_response = Gitea::User::Keys::CreateService.call(current_user.gitea_token, public_key_params) | |||
| if @gitea_response[0] == 201 | |||
| @public_key = @gitea_response[2] | |||
| else | |||
| return render_error("创建ssh key失败") if @gitea_response[2].blank? | |||
| return render_ok({status: 10002, message: "密钥格式不正确"}) if @gitea_response[2]["message"].starts_with?("Invalid key content") | |||
| exist_public_key = Gitea::PublicKey.find_by(content: public_key_params[:key]) | |||
| return render_ok({status: 10002, message: "密钥已被占用"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key") && exist_public_key.present? && exist_public_key&.owner_id != current_user.gitea_uid | |||
| return render_ok({status: 10002, message: "密钥已存在,请勿重复添加"}) if @gitea_response[2]["message"].starts_with?("Key content has been used as non-deploy key") | |||
| @public_key = nil | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def destroy | |||
| return render_not_found unless @public_key.present? | |||
| result = Gitea::User::Keys::DeleteService.call(current_user.gitea_token, @public_key.id) | |||
| if result[0] == 204 | |||
| render_ok | |||
| else | |||
| render_error | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| private | |||
| def page | |||
| params[:page].to_i.zero? ? 1 : params[:page].to_i | |||
| end | |||
| def limit | |||
| limit = params[:limit] || params[:per_page] | |||
| limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i | |||
| end | |||
| def public_key_params | |||
| params.require(:public_key).permit(:key, :title) | |||
| end | |||
| def find_public_key | |||
| @public_key = current_user.public_keys.find_by_id(params[:id]) | |||
| end | |||
| end | |||
| @@ -1,14 +1,16 @@ | |||
| class PullRequestsController < ApplicationController | |||
| before_action :require_login, except: [:index, :show, :files, :commits] | |||
| before_action :require_profile_completed, only: [:create] | |||
| before_action :load_repository | |||
| before_action :check_menu_authorize | |||
| before_action :find_pull_request, except: [:index, :new, :create, :check_can_merge,:get_branches,:create_merge_infos, :files, :commits] | |||
| before_action :load_pull_request, only: [:files, :commits] | |||
| before_action :find_atme_receivers, only: [:create, :update] | |||
| include TagChosenHelper | |||
| include ApplicationHelper | |||
| def index | |||
| return render_not_found unless @project.has_menu_permission("pulls") | |||
| # @issues = Gitea::PullRequest::ListService.new(@user,@repository.try(:identifier)).call #通过gitea获取 | |||
| issues = @project.issues.issue_pull_request.issue_index_includes.includes(pull_request: :user) | |||
| issues = issues.where(is_private: false) unless current_user.present? && (current_user.admin? || @project.member?(current_user)) | |||
| @@ -19,6 +21,7 @@ class PullRequestsController < ApplicationController | |||
| @close_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::CLOSED}) | |||
| @merged_issues = @filter_issues.joins(:pull_request).where(pull_requests: {status: PullRequest::MERGED}) | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| @user_admin_or_developer = current_user.present? && (current_user.admin || @project.all_developers.include?(current_user)) | |||
| scopes = Issues::ListQueryService.call(issues,params.delete_if{|k,v| v.blank?}, "PullRequest") | |||
| @issues_size = scopes.size | |||
| @@ -56,8 +59,11 @@ class PullRequestsController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| @pull_request, @gitea_pull_request = PullRequests::CreateService.call(current_user, @owner, @project, params) | |||
| if @gitea_pull_request[:status] == :success | |||
| @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"]) | |||
| render_ok | |||
| @pull_request.bind_gitea_pull_request!(@gitea_pull_request[:body]["number"], @gitea_pull_request[:body]["id"]) | |||
| SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('ProjectPullRequest', current_user.id, @pull_request&.id) if Site.has_notice_menu? | |||
| Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}" | |||
| AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0 | |||
| else | |||
| render_error("create pull request error: #{@gitea_pull_request[:status]}") | |||
| raise ActiveRecord::Rollback | |||
| @@ -66,21 +72,22 @@ class PullRequestsController < ApplicationController | |||
| end | |||
| def edit | |||
| @fork_project_user_name = @project&.fork_project&.owner.try(:show_real_name) | |||
| @fork_project_user = @project&.fork_project&.owner.try(:login) | |||
| @fork_project_identifier = @project&.fork_project&.repository.try(:identifier) | |||
| @fork_project_user_name = @pull_request&.fork_project&.owner.try(:show_real_name) | |||
| @fork_project_user = @pull_request&.fork_project&.owner.try(:login) | |||
| @fork_project_identifier = @pull_request&.fork_project&.repository.try(:identifier) | |||
| end | |||
| def update | |||
| if params[:title].nil? | |||
| normal_status(-1, "名称不能为空") | |||
| elsif params[:issue_tag_ids].nil? | |||
| normal_status(-1, "标签不能为空") | |||
| normal_status(-1, "标记不能为空") | |||
| else | |||
| ActiveRecord::Base.transaction do | |||
| begin | |||
| merge_params | |||
| @issue&.issue_tags_relates&.destroy_all if params[:issue_tag_ids].blank? | |||
| if params[:issue_tag_ids].present? && !@issue&.issue_tags_relates.where(issue_tag_id: params[:issue_tag_ids]).exists? | |||
| @issue&.issue_tags_relates&.destroy_all | |||
| params[:issue_tag_ids].each do |tag| | |||
| @@ -91,7 +98,7 @@ class PullRequestsController < ApplicationController | |||
| if @issue.update_attributes(@issue_params) | |||
| if @pull_request.update_attributes(@local_params.compact) | |||
| gitea_pull = Gitea::PullRequest::UpdateService.call(@owner.login, @repository.identifier, | |||
| @pull_request.gpid, @requests_params, current_user.gitea_token) | |||
| @pull_request.gitea_number, @requests_params, current_user.gitea_token) | |||
| if gitea_pull[:status] === :success | |||
| if params[:issue_tag_ids].present? | |||
| @@ -102,6 +109,8 @@ class PullRequestsController < ApplicationController | |||
| if params[:status_id].to_i == 5 | |||
| @issue.issue_times.update_all(end_time: Time.now) | |||
| end | |||
| Rails.logger.info "[ATME] maybe to at such users: #{@atme_receivers.pluck(:login)}" | |||
| AtmeService.call(current_user, @atme_receivers, @pull_request) if @atme_receivers.size > 0 | |||
| normal_status(0, "PullRequest更新成功") | |||
| else | |||
| normal_status(-1, "PullRequest更新失败") | |||
| @@ -114,6 +123,8 @@ class PullRequestsController < ApplicationController | |||
| normal_status(-1, e.message) | |||
| raise ActiveRecord::Rollback | |||
| end | |||
| SendTemplateMessageJob.perform_later('PullRequestChanged', current_user.id, @pull_request&.id, @issue.previous_changes.slice(:assigned_to_id, :priority_id, :fixed_version_id, :issue_tags_value)) if Site.has_notice_menu? | |||
| SendTemplateMessageJob.perform_later('PullRequestAssigned', current_user.id, @pull_request&.id ) if @issue.previous_changes[:assigned_to_id].present? && Site.has_notice_menu? | |||
| end | |||
| end | |||
| @@ -123,7 +134,13 @@ class PullRequestsController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| begin | |||
| colsed = PullRequests::CloseService.call(@owner, @repository, @pull_request, current_user) | |||
| colsed === true ? normal_status(1, "已拒绝") : normal_status(-1, '合并失败') | |||
| if colsed === true | |||
| @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::CLOSE) | |||
| SendTemplateMessageJob.perform_later('PullRequestClosed', current_user.id, @pull_request.id) if Site.has_notice_menu? | |||
| normal_status(1, "已拒绝") | |||
| else | |||
| normal_status(-1, '合并失败') | |||
| end | |||
| rescue => e | |||
| normal_status(-1, e.message) | |||
| raise ActiveRecord::Rollback | |||
| @@ -139,7 +156,7 @@ class PullRequestsController < ApplicationController | |||
| @issue_user = @issue.user | |||
| @issue_assign_to = @issue.get_assign_user | |||
| @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, | |||
| @repository.identifier, @pull_request.gpid, current_user&.gitea_token) | |||
| @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token) | |||
| end | |||
| def pr_merge | |||
| @@ -150,11 +167,20 @@ class PullRequestsController < ApplicationController | |||
| else | |||
| ActiveRecord::Base.transaction do | |||
| begin | |||
| result = PullRequests::MergeService.call(@owner, @repository, @pull_request, current_user, params) | |||
| @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token) | |||
| if @gitea_pull["merged_by"].present? | |||
| success_condition = true | |||
| else | |||
| result = PullRequests::MergeService.call(@owner, @repository, @pull_request, current_user, params) | |||
| success_condition = result.status == 200 | |||
| end | |||
| if result.status == 200 && @pull_request.merge! | |||
| @pull_request.project_trend_status! | |||
| if success_condition && @pull_request.merge! | |||
| # @pull_request.project_trend_status! | |||
| @pull_request.project_trends.create!(user: current_user, project: @project,action_type: ProjectTrend::MERGE) | |||
| @issue&.custom_journal_detail("merge", "", "该合并请求已被合并", current_user&.id) | |||
| SendTemplateMessageJob.perform_later('PullRequestMerged', current_user.id, @pull_request.id) if Site.has_notice_menu? | |||
| normal_status(1, "合并成功") | |||
| else | |||
| normal_status(-1, result.message) | |||
| @@ -181,7 +207,7 @@ class PullRequestsController < ApplicationController | |||
| if can_merge.present? | |||
| render json: { | |||
| status: -2, | |||
| message: "在这些分支之间的合并请求已存在:<a href='/projects/#{@owner.login}/#{@project.identifier}/pulls/#{can_merge.first.id}/Messagecount''>#{can_merge.first.try(:title)}</a>", | |||
| message: "在这些分支之间的合并请求已存在:<a href='/#{@owner.login}/#{@project.identifier}/pulls/#{can_merge.first.id}''>#{can_merge.first.try(:title)}</a>", | |||
| } | |||
| else | |||
| normal_status(0, "可以合并") | |||
| @@ -191,12 +217,12 @@ class PullRequestsController < ApplicationController | |||
| def files | |||
| @files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gpid, current_user&.gitea_token) | |||
| @files_result = Gitea::PullRequest::FilesService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token) | |||
| # render json: @files_result | |||
| end | |||
| def commits | |||
| @commits_result = Gitea::PullRequest::CommitsService.call(@owner.login, @project.identifier, @pull_request.gpid, current_user&.gitea_token) | |||
| @commits_result = Gitea::PullRequest::CommitsService.call(@owner.login, @project.identifier, @pull_request.gitea_number, current_user&.gitea_token) | |||
| # render json: @commits_result | |||
| end | |||
| @@ -252,4 +278,8 @@ class PullRequestsController < ApplicationController | |||
| status_id: 1, | |||
| } | |||
| end | |||
| def check_menu_authorize | |||
| return render_not_found unless @project.has_menu_permission("pulls") | |||
| end | |||
| end | |||
| @@ -1,13 +1,15 @@ | |||
| class RepositoriesController < ApplicationController | |||
| include RepositoriesHelper | |||
| include ApplicationHelper | |||
| include OperateProjectAbilityAble | |||
| include Repository::LanguagesPercentagable | |||
| before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror] | |||
| before_action :require_profile_completed, only: [:create_file] | |||
| before_action :load_repository | |||
| before_action :authorizate!, except: [:sync_mirror, :tags, :commit] | |||
| before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive] | |||
| before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror] | |||
| before_action :get_ref, only: %i[entries sub_entries top_counts file] | |||
| before_action :get_ref, only: %i[entries sub_entries top_counts file archive] | |||
| before_action :get_latest_commit, only: %i[entries sub_entries top_counts] | |||
| before_action :get_statistics, only: %i[top_counts] | |||
| @@ -46,23 +48,13 @@ class RepositoriesController < ApplicationController | |||
| def entries | |||
| @project.increment!(:visits) | |||
| CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id) | |||
| if @project.educoder? | |||
| @entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name) | |||
| else | |||
| @entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call | |||
| @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : [] | |||
| @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" | |||
| # TODO | |||
| # 临时处理readme文件问题 | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @project.identifier, @ref, @owner&.gitea_token) | |||
| @readme = | |||
| if result[:status] == :success | |||
| result[:body] | |||
| else | |||
| {} | |||
| end | |||
| end | |||
| end | |||
| @@ -72,6 +64,7 @@ class RepositoriesController < ApplicationController | |||
| def sub_entries | |||
| file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) | |||
| @path = Gitea.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/" | |||
| if @project.educoder? | |||
| if params[:type] === 'file' | |||
| @@ -102,10 +95,21 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def commits | |||
| @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, | |||
| sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call | |||
| if params[:filepath].present? | |||
| file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) | |||
| @hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri, | |||
| sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call | |||
| else | |||
| @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier, | |||
| sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token).call | |||
| end | |||
| end | |||
| def commits_slice | |||
| @hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier, | |||
| sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token) | |||
| end | |||
| def commit | |||
| @sha = params[:sha] | |||
| @commit = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, current_user&.gitea_token) | |||
| @@ -119,7 +123,11 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def contributors | |||
| @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) | |||
| if params[:filepath].present? | |||
| @contributors = [] | |||
| else | |||
| @contributors = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier) | |||
| end | |||
| end | |||
| def edit | |||
| @@ -182,16 +190,46 @@ class RepositoriesController < ApplicationController | |||
| end | |||
| def readme | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) | |||
| if params[:filepath].present? | |||
| result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], current_user&.gitea_token) | |||
| else | |||
| result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], current_user&.gitea_token) | |||
| end | |||
| @readme = result[:status] === :success ? result[:body] : nil | |||
| render json: @readme | |||
| @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref]) | |||
| render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha") | |||
| rescue | |||
| render json: nil | |||
| end | |||
| def languages | |||
| render json: languages_precentagable | |||
| end | |||
| def archive | |||
| domain = Gitea.gitea_config[:domain] | |||
| api_url = Gitea.gitea_config[:base_url] | |||
| archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{params[:archive]}" | |||
| file_path = [domain, api_url, archive_url].join | |||
| file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("?") if @repository.hidden? | |||
| return render_not_found if !request.format.zip? && !request.format.gzip? | |||
| redirect_to file_path | |||
| end | |||
| def raw | |||
| domain = Gitea.gitea_config[:domain] | |||
| api_url = Gitea.gitea_config[:base_url] | |||
| url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{params[:filepath]}?ref=#{params[:ref]}" | |||
| file_path = [domain, api_url, url].join | |||
| file_path = [file_path, "access_token=#{current_user&.gitea_token}"].join("&") if @repository.hidden? | |||
| redirect_to URI.escape(file_path) | |||
| end | |||
| private | |||
| def find_project | |||
| @@ -212,8 +250,14 @@ class RepositoriesController < ApplicationController | |||
| # TODO 获取最新commit信息 | |||
| def project_commits | |||
| Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, | |||
| sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call | |||
| if params[:filepath].present? | |||
| file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip)) | |||
| Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri, | |||
| sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call | |||
| else | |||
| Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier, | |||
| sha: get_ref, page: 1, limit: 1, token: current_user&.gitea_token).call | |||
| end | |||
| end | |||
| def get_statistics | |||
| @@ -266,7 +310,7 @@ class RepositoriesController < ApplicationController | |||
| # uploadPushInfo | |||
| end | |||
| def create_new_pr(params) | |||
| if params[:new_branch].present? && params[:new_branch] != params[:branch] | |||
| local_params = { | |||
| @@ -4,7 +4,7 @@ class SettingsController < ApplicationController | |||
| get_add_menu | |||
| get_common_menu | |||
| get_personal_menu | |||
| get_top_system_notification | |||
| end | |||
| private | |||
| @@ -40,6 +40,10 @@ class SettingsController < ApplicationController | |||
| end | |||
| end | |||
| def get_top_system_notification | |||
| @top_system_notification = SystemNotification.is_top.first | |||
| end | |||
| def get_site_url(key, value) | |||
| key.to_s === "url" ? append_http(reset_site_url(value)) : reset_site_url(value) | |||
| end | |||
| @@ -0,0 +1,8 @@ | |||
| class TemplateMessageSettingsController < ApplicationController | |||
| before_action :require_login | |||
| def index | |||
| @group_settings = TemplateMessageSetting.group(:type).count | |||
| end | |||
| end | |||
| @@ -0,0 +1,24 @@ | |||
| class UserRankController < ApplicationController | |||
| # 根据时间获取热门开发者 | |||
| def index | |||
| $redis_cache.zunionstore("recent-days-user-rank", get_timeable_key_names) | |||
| @user_rank = $redis_cache.zrevrange("recent-days-user-rank", 0, 3, withscores: true) | |||
| rescue Exception => e | |||
| @user_rank = [] | |||
| end | |||
| private | |||
| # 默认显示7天的 | |||
| def time | |||
| params.fetch(:time, 7).to_i | |||
| end | |||
| def get_timeable_key_names | |||
| names_array = [] | |||
| (0...time).to_a.each do |i| | |||
| date_time_string = (Date.today - i.days).to_s | |||
| names_array << "v2-user-rank-#{date_time_string}" | |||
| end | |||
| names_array | |||
| end | |||
| end | |||
| @@ -5,7 +5,7 @@ class Users::BaseController < ApplicationController | |||
| helper_method :observed_logged_user?, :observed_user | |||
| def observed_user | |||
| @_observed_user ||= (User.find_by_id(params[:user_id]) || User.find_by_login(params[:user_id])) | |||
| @_observed_user ||= (User.find_by_login(params[:user_id]) || User.find_by_id(params[:user_id])) | |||
| end | |||
| def observed_logged_user? | |||
| @@ -1,7 +1,7 @@ | |||
| class Users::HeadmapsController < Users::BaseController | |||
| def index | |||
| result = Gitea::User::HeadmapService.call(observed_user.login, start_stamp, end_stamp) | |||
| @headmaps = result[2] | |||
| @headmaps = result[2].blank? ? [] : result[2] | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| @@ -0,0 +1,96 @@ | |||
| class Users::MessagesController < Users::BaseController | |||
| before_action :private_user_resources! | |||
| before_action :find_receivers, only: [:create] | |||
| def index | |||
| limit = params[:limit] || params[:per_page] | |||
| limit = (limit.to_i.zero? || limit.to_i > 15) ? 15 : limit.to_i | |||
| page = params[:page].to_i.zero? ? 1 : params[:page].to_i | |||
| result = Notice::Read::ListService.call(observed_user.id, message_type, message_status, page, limit) | |||
| return render_error if result.nil? | |||
| @data = result[2] | |||
| end | |||
| def create | |||
| return render_forbidden unless %w(atme).include?(params[:type]) | |||
| case params[:type] | |||
| when 'atme' | |||
| Notice::Write::CreateAtmeForm.new(atme_params).validate! | |||
| case atme_params[:atmeable_type] | |||
| when 'Issue' | |||
| SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu? | |||
| when 'PullRequest' | |||
| SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu? | |||
| when 'Journal' | |||
| journal = Journal.find_by_id(atme_params[:atmeable_id]) | |||
| if journal.present? | |||
| if journal&.issue&.pull_request.present? | |||
| SendTemplateMessageJob.perform_now('PullRequestAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu? | |||
| else | |||
| SendTemplateMessageJob.perform_now('IssueAtme', @receivers, current_user.id, atme_params[:atmeable_id]) if Site.has_notice_menu? | |||
| end | |||
| end | |||
| end | |||
| end | |||
| render_ok | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def delete | |||
| return render_forbidden unless %w(atme).include?(params[:type]) | |||
| result = Notice::Write::DeleteService.call(params[:ids], observed_user.id, message_type) | |||
| return render_error if result.nil? | |||
| render_ok | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| def read | |||
| return render_forbidden unless %w(notification atme).include?(params[:type]) | |||
| result = Notice::Write::ChangeStatusService.call(params[:ids], observed_user.id, message_type) | |||
| if result.nil? | |||
| render_error | |||
| else | |||
| render_ok | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| private | |||
| def message_type | |||
| @message_type = begin | |||
| case params[:type] | |||
| when "notification" then 1 | |||
| when "atme" then 2 | |||
| else | |||
| -1 | |||
| end | |||
| end | |||
| end | |||
| def message_status | |||
| @message_status = begin | |||
| case params[:status] | |||
| when "1" then 1 | |||
| when "2" then 2 | |||
| else | |||
| -1 | |||
| end | |||
| end | |||
| end | |||
| def atme_params | |||
| params.permit(:atmeable_type, :atmeable_id, receivers_login: []) | |||
| end | |||
| def find_receivers | |||
| @receivers = User.where(login: params[:receivers_login]) | |||
| return render_not_found if @receivers.size == 0 | |||
| end | |||
| end | |||
| @@ -14,7 +14,7 @@ class Users::StatisticsController < Users::BaseController | |||
| @date_data << date.strftime("%Y.%m.%d") | |||
| @issue_data << observed_user.issues.where("DATE(created_on) = ?", date).size | |||
| @pull_request_data << observed_user.pull_requests.where("DATE(created_at) = ?", date).size | |||
| date_commit_data = commit_data.select{|item| item["timestamp"] == date.to_time.to_i} | |||
| date_commit_data = commit_data.blank? ? nil : commit_data.select{|item| item["timestamp"] == date.to_time.to_i} | |||
| @commit_data << (date_commit_data.blank? ? 0 : date_commit_data[0]["contributions"].to_i) | |||
| end | |||
| render :json => {dates: @date_data, issues_count: @issue_data, pull_requests_count: @pull_request_data, commits_count: @commit_data} | |||
| @@ -188,30 +188,32 @@ class Users::StatisticsController < Users::BaseController | |||
| @project_languages_count = time_filter(Project.where(user_id: observed_user.id), 'created_on').joins(:project_language).group("project_languages.name").count | |||
| @platform_project_languages_count = time_filter(Project, 'created_on').joins(:project_language).group("project_languages.name").count | |||
| else | |||
| @platform_result = Cache::V2::PlatformStatisticService.new.read | |||
| @user_result = Cache::V2::UserStatisticService.new(observed_user.id).read | |||
| # 用户被follow数量 | |||
| @follow_count = Cache::UserFollowCountService.call(observed_user) | |||
| @platform_follow_count = Cache::PlatformFollowCountService.call | |||
| @follow_count = @user_result["follow-count"].to_i | |||
| @platform_follow_count = @platform_result["follow-count"].to_i | |||
| # 用户pr数量 | |||
| @pullrequest_count = Cache::UserPullrequestCountService.call(observed_user) | |||
| @platform_pullrequest_count = Cache::PlatformPullrequestCountService.call | |||
| @pullrequest_count = @user_result["pullrequest-count"].to_i | |||
| @platform_pullrequest_count = @platform_result["pullrequest-count"].to_i | |||
| # 用户issue数量 | |||
| @issues_count = Cache::UserIssueCountService.call(observed_user) | |||
| @platform_issues_count = Cache::PlatformIssueCountService.call | |||
| @issues_count = @user_result["issue-count"].to_i | |||
| @platform_issues_count = @platform_result["issue-count"].to_i | |||
| # 用户总项目数 | |||
| @project_count = Cache::UserProjectCountService.call(observed_user) | |||
| @platform_project_count = Cache::PlatformProjectCountService.call | |||
| @project_count = @user_result["project-count"].to_i | |||
| @platform_project_count = @platform_result["project-count"].to_i | |||
| # 用户项目被fork数量 | |||
| @fork_count = Cache::UserProjectForkCountService.call(observed_user) | |||
| @platform_fork_count = Cache::PlatformProjectForkCountService.call | |||
| @fork_count = @user_result["fork-count"].to_i | |||
| @platform_fork_count = @platform_result["fork-count"].to_i | |||
| # 用户项目关注数 | |||
| @project_watchers_count = Cache::UserProjectWatchersCountService.call(observed_user) | |||
| @platform_project_watchers_count = Cache::PlatformProjectWatchersCountService.call | |||
| @project_watchers_count = @user_result["project-watcher-count"].to_i | |||
| @platform_project_watchers_count = @platform_result["project-watcher-count"].to_i | |||
| # 用户项目点赞数 | |||
| @project_praises_count = Cache::UserProjectPraisesCountService.call(observed_user) | |||
| @platform_project_praises_count = Cache::PlatformProjectPraisesCountService.call | |||
| @project_praises_count = @user_result["project-praise-count"].to_i | |||
| @platform_project_praises_count = @platform_result["project-praise-count"].to_i | |||
| # 用户不同语言项目数量 | |||
| @project_languages_count = Cache::UserProjectLanguagesCountService.call(observed_user) | |||
| @platform_project_languages_count = Cache::PlatformProjectLanguagesCountService.call | |||
| @project_languages_count = JSON.parse(@user_result["project-language"]) | |||
| @platform_project_languages_count = JSON.parse(@platform_result["project-language"]) | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,15 @@ | |||
| class Users::SystemNotificationHistoriesController < Users::BaseController | |||
| before_action :private_user_resources!, only: [:create] | |||
| def create | |||
| @history = observed_user.system_notification_histories.new(system_notification_id: params[:system_notification_id]) | |||
| if @history.save | |||
| render_ok | |||
| else | |||
| Rails.logger.info @history.errors.as_json | |||
| render_error(@history.errors.full_messages.join(",")) | |||
| end | |||
| rescue Exception => e | |||
| uid_logger_error(e.message) | |||
| tip_exception(e.message) | |||
| end | |||
| end | |||
| @@ -0,0 +1,36 @@ | |||
| class Users::TemplateMessageSettingsController < Users::BaseController | |||
| before_action :check_auth | |||
| before_action :get_current_setting | |||
| def current_setting | |||
| end | |||
| def update_setting | |||
| Rails.logger.info setting_params[:notification_body] | |||
| Rails.logger.info setting_params[:email_body] | |||
| @current_setting.notification_body = setting_params[:notification_body].to_hash | |||
| @current_setting.email_body = setting_params[:email_body].to_hash | |||
| return render_error("保存失败") unless @current_setting.save! | |||
| end | |||
| private | |||
| def check_auth | |||
| return render_forbidden unless current_user.admin? || observed_logged_user? | |||
| end | |||
| def get_current_setting | |||
| @current_setting = @_observed_user.user_template_message_setting | |||
| @current_setting = UserTemplateMessageSetting.build(@_observed_user.id) if @current_setting.nil? | |||
| end | |||
| def setting_params | |||
| params.require(:setting).permit(notification_body: {}, email_body: {}) | |||
| end | |||
| def valid_setting_params | |||
| setting_params[:notification_body].keys.equal?(UserTemplateMessageSetting.init_notification_body.keys) && setting_params[:email_body].keys.equal?(UserTemplateMessageSetting.init_email_body) | |||
| end | |||
| end | |||
| @@ -51,6 +51,8 @@ class UsersController < ApplicationController | |||
| @projects_common_count = user_projects.common.size | |||
| @projects_mirrior_count = user_projects.mirror.size | |||
| @projects_sync_mirrior_count = user_projects.sync_mirror.size | |||
| # 为了缓存活跃用户的基本信息,后续删除 | |||
| Cache::V2::OwnerCommonService.new(@user.id).read | |||
| end | |||
| def watch_users | |||
| @@ -74,7 +76,7 @@ class UsersController < ApplicationController | |||
| end | |||
| def update | |||
| return render_not_found unless @user = User.find_by_id(params[:id]) || User.find_by(login: params[:id]) | |||
| return render_not_found unless @user = User.find_by(login: params[:id]) || User.find_by_id(params[:id]) | |||
| return render_forbidden unless User.current.logged? && (current_user&.admin? || current_user.id == @user.id) | |||
| Util.write_file(@image, avatar_path(@user)) if user_params[:image].present? | |||
| @user.attributes = user_params.except(:image) | |||
| @@ -91,6 +93,12 @@ class UsersController < ApplicationController | |||
| def get_user_info | |||
| begin | |||
| @user = current_user | |||
| begin | |||
| result = Notice::Read::CountService.call(current_user.id) | |||
| @message_unread_total = result.nil? ? 0 : result[2]["unread_notification"] | |||
| rescue | |||
| @message_unread_total = 0 | |||
| end | |||
| # TODO 等消息上线再打开注释 | |||
| #@tidding_count = unviewed_tiddings(current_user) if current_user.present? | |||
| rescue Exception => e | |||
| @@ -185,7 +193,7 @@ class UsersController < ApplicationController | |||
| def trustie_related_projects | |||
| projects = Project.includes(:owner, :members, :project_score).where(id: params[:ids]).order("updated_on desc") | |||
| projects_json = [] | |||
| domain_url = EduSetting.get('host_name') + '/projects' | |||
| domain_url = EduSetting.get('host_name') | |||
| if projects.present? | |||
| projects.each do |p| | |||
| project_url = "/#{p.owner.login}/#{p.identifier}" | |||
| @@ -1,14 +1,14 @@ | |||
| class VersionReleasesController < ApplicationController | |||
| before_action :load_repository | |||
| before_action :set_user | |||
| before_action :require_login, except: [:index] | |||
| before_action :find_version , only: [:edit, :update, :destroy] | |||
| before_action :require_login, except: [:index, :show] | |||
| before_action :check_release_authorize, except: [:index, :show] | |||
| before_action :find_version , only: [:show, :edit, :update, :destroy] | |||
| def index | |||
| version_releases = Gitea::Versions::ListService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier)).call | |||
| @version_releases = version_releases | |||
| @user_permission = current_user.present? && (current_user == @user || current_user.admin?) | |||
| @forge_releases = @repository.version_releases.select(:id,:version_gid, :created_at).includes(:attachments) | |||
| @version_releases = kaminari_paginate(@repository.version_releases.order(created_at: :desc)) | |||
| @user_permission = current_user.present? && (@repository.project.all_developers.include?(current_user) || current_user.admin?) | |||
| @user_admin_permission = current_user.present? && (@repository.project.all_managers.include?(current_user) || current_user.admin?) | |||
| end | |||
| def new | |||
| @@ -22,6 +22,10 @@ class VersionReleasesController < ApplicationController | |||
| end | |||
| end | |||
| def show | |||
| # @release = Gitea::Versions::GetService.call(current_user.gitea_token, @user&.login, @repository&.identifier, @version&.version_gid) | |||
| end | |||
| def create | |||
| if params[:name].nil? | |||
| normal_status(-1, "名称不能为空") | |||
| @@ -37,13 +41,14 @@ class VersionReleasesController < ApplicationController | |||
| version_params = releases_params | |||
| version_release = VersionRelease.new(version_params.merge(user_id: current_user.id, repository_id: @repository.id)) | |||
| if version_release.save! | |||
| git_version_release = Gitea::Versions::CreateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call | |||
| git_version_release = Gitea::Versions::CreateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params).call | |||
| if git_version_release | |||
| update_params = { | |||
| tarball_url: git_version_release["tarball_url"], | |||
| zipball_url: git_version_release["zipball_url"], | |||
| url: git_version_release["url"], | |||
| version_gid: git_version_release["id"], | |||
| sha: git_version_release["sha"] | |||
| } | |||
| version_release.update_attributes!(update_params) | |||
| version_release.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create") | |||
| @@ -81,7 +86,7 @@ class VersionReleasesController < ApplicationController | |||
| if @version.update_attributes!(version_params) | |||
| create_attachments(params[:attachment_ids], @version) if params[:attachment_ids].present? | |||
| git_version_release = Gitea::Versions::UpdateService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call | |||
| git_version_release = Gitea::Versions::UpdateService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), version_params, @version.try(:version_gid)).call | |||
| unless git_version_release | |||
| raise Error, "更新失败" | |||
| end | |||
| @@ -102,7 +107,7 @@ class VersionReleasesController < ApplicationController | |||
| ActiveRecord::Base.transaction do | |||
| begin | |||
| if @version.destroy | |||
| git_version_release = Gitea::Versions::DeleteService.new(@user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call | |||
| git_version_release = Gitea::Versions::DeleteService.new(current_user.gitea_token, @user.try(:login), @repository.try(:identifier), @version.try(:version_gid)).call | |||
| if git_version_release.status == 204 | |||
| normal_status(0, "删除成功") | |||
| @@ -157,4 +162,8 @@ class VersionReleasesController < ApplicationController | |||
| end | |||
| end | |||
| def check_release_authorize | |||
| return render_forbidden("您没有权限进行此操作.") unless current_user.admin? || @project.manager?(current_user) | |||
| end | |||
| end | |||
| @@ -1,11 +1,12 @@ | |||
| class VersionsController < ApplicationController | |||
| before_action :require_login, except: [:index, :show] | |||
| before_action :require_profile_completed, only: [:create] | |||
| before_action :load_repository | |||
| before_action :check_menu_authorize | |||
| before_action :check_issue_permission, except: [:show, :index] | |||
| before_action :set_version, only: [:edit, :update, :destroy, :show,:update_status] | |||
| def index | |||
| return render_not_found unless @project.has_menu_permission("versions") | |||
| @user_admin_or_member = current_user.present? && (current_user.admin || @project.member?(current_user)) | |||
| status = params[:status] | |||
| versions = @project.versions.version_includes | |||
| @@ -25,17 +26,13 @@ class VersionsController < ApplicationController | |||
| end | |||
| def show | |||
| version_issues = @version.issues.issue_includes | |||
| version_issues = @version.issues.issue_issue.issue_includes | |||
| status_type = params[:status_type] || "1" | |||
| # @close_issues_size = version_issues.where(status_id: 5).size | |||
| # @open_issues_size = version_issues.size - @close_issues_size | |||
| if status_type.to_s == "1" #表示开启中的 | |||
| version_issues = version_issues.where.not(status_id: 5) | |||
| else | |||
| version_issues = version_issues.where(status_id: 5) | |||
| end | |||
| version_issues = version_issues.where(author_id: params[:author_id]) if params[:author_id].present? && params[:author_id].to_s != "all" | |||
| version_issues = version_issues.where(assigned_to_id: params[:assigned_to_id]) if params[:assigned_to_id].present? && params[:assigned_to_id].to_s != "all" | |||
| version_issues = version_issues.where(tracker_id: params[:tracker_id]) if params[:tracker_id].present? && params[:tracker_id].to_s != "all" | |||
| @@ -47,10 +44,26 @@ class VersionsController < ApplicationController | |||
| version_issues = version_issues.joins(:issue_tags).where(issue_tags: {id: params[:issue_tag_id].to_i}) if params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all" | |||
| version_issues = version_issues.reorder("#{order_name} #{order_type}") | |||
| has_filter_params = (params[:author_id].present? && params[:author_id].to_s != "all") || | |||
| (params[:assigned_to_id].present? && params[:assigned_to_id].to_s != "all") || | |||
| (params[:tracker_id].present? && params[:tracker_id].to_s != "all") || | |||
| (params[:status_id].present? && params[:status_id].to_s != "all") || | |||
| (params[:priority_id].present? && params[:priority_id].to_s != "all") || | |||
| (params[:fixed_version_id].present? && params[:fixed_version_id].to_s != "all") || | |||
| (params[:done_ratio].present? && params[:done_ratio].to_s != "all") || | |||
| (params[:issue_type].present? && params[:issue_type].to_s != "all") || | |||
| (params[:issue_tag_id].present? && params[:issue_tag_id].to_s != "all") | |||
| @version_close_issues_size = has_filter_params ? version_issues.closed.size : @version.issues.issue_issue.issue_includes.closed.size | |||
| @version_issues_size = has_filter_params ? version_issues.size : @version.issues.issue_issue.issue_includes.size | |||
| if status_type.to_s == "1" #表示开启中的 | |||
| version_issues = version_issues.where.not(status_id: 5) | |||
| else | |||
| version_issues = version_issues.where(status_id: 5) | |||
| end | |||
| @page = params[:page] || 1 | |||
| @limit = params[:limit] || 15 | |||
| @version_issues_size = version_issues.size | |||
| # @version_issues_size = version_issues.size | |||
| @version_issues = version_issues.page(@page).per(@limit) | |||
| end | |||
| @@ -170,4 +183,8 @@ class VersionsController < ApplicationController | |||
| %w(desc asc).include?(params[:order_type]) ? params[:order_type] : 'desc' | |||
| end | |||
| def check_menu_authorize | |||
| return render_not_found unless @project.has_menu_permission("versions") | |||
| end | |||
| end | |||
| @@ -1,5 +1,6 @@ | |||
| class WatchersController < ApplicationController | |||
| before_action :require_login, except: %i[index] | |||
| before_action :require_profile_completed, only: [:follow] | |||
| # before_action :find_project_with_id | |||
| before_action :get_target | |||
| @@ -1,5 +0,0 @@ | |||
| module CourseDecorator | |||
| def can_visited? | |||
| is_public == 1 || User.current.admin_or_business? || User.current.member_of_course?(self) | |||
| end | |||
| end | |||
| @@ -1,2 +0,0 @@ | |||
| module EcCourseTargetDecorator | |||
| end | |||
| @@ -1,16 +0,0 @@ | |||
| module ExperienceDecorator | |||
| def container_type_text | |||
| I18n.t("experience.container_type.#{container_type.to_s.underscore}") | |||
| end | |||
| def content | |||
| case container_type.to_s.underscore | |||
| when 'game' then | |||
| game = Game.find_by(id: container_id) | |||
| game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : '' | |||
| when 'shixun_publish' then | |||
| shixun = Shixun.find_by(id: container_id) | |||
| shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : '' | |||
| end | |||
| end | |||
| end | |||
| @@ -1,39 +0,0 @@ | |||
| module GradeDecorator | |||
| def container_type_text | |||
| I18n.t("grade.container_type.#{container_type.to_s.underscore}") | |||
| end | |||
| def content | |||
| case container_type.to_s.underscore | |||
| when 'avatar' then '用户首次上传头像获得的奖励' | |||
| when 'phone' then '用户首次绑定手机号码获得的奖励' | |||
| when 'mail' then '用户首次绑定邮箱获得的奖励' | |||
| when 'attendance' then '用户每天签到获得的奖励' | |||
| when 'account' then '新用户首次填写基本资料获得的奖励' | |||
| when 'memo' then '发布的评论或者帖子获得平台奖励' | |||
| when 'discusses' then '发布的评论获得平台奖励' | |||
| when 'star' then '用户给实训评分获得的随机奖励' | |||
| when 'feedback' then '反馈的问题获得平台奖励' | |||
| when 'authentication' then '用户首次完成实名认证获得的奖励' | |||
| when 'professional' then '用户首次完成职业认证获得的奖励' | |||
| when 'answer' then | |||
| game = Game.find_by(id: container_id) | |||
| game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的参考答案消耗的金币" : '' | |||
| when 'game' then | |||
| game = Game.find_by(id: container_id) | |||
| game.present? ? "通过实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关获得的奖励" : '' | |||
| when 'test_set' then | |||
| game = Game.find_by(id: container_id) | |||
| game.present? ? "查看实训“#{game.challenge.shixun.name}”的第#{game.challenge.position}关的隐藏测试集消耗的金币" : '' | |||
| when 'shixun_publish' then | |||
| shixun = Shixun.find_by(id: container_id) | |||
| shixun.present? ? "发布实训“#{shixun.name}”获得的奖励" : '' | |||
| when 'check_ta_answer' then | |||
| game = Game.find_by(id: container_id) | |||
| game.present? ? "查看实训“#{game.challenge.shixun.name}”第#{game.challenge.position}关的TA人解答消耗的金币" : '' | |||
| when 'hack' then | |||
| hack = Hack.find_by(id: container_id) | |||
| hack.present? ? "完成了题目解答“#{hack.name}”,获得金币奖励:#{hack.score}" : '' | |||
| end | |||
| end | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module LibraryDecorator | |||
| extend ApplicationDecorator | |||
| display_time_method :published_at, :created_at, :updated_at | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module ShixunDecorator | |||
| def human_status | |||
| I18n.t("shixun.status.#{status}") | |||
| end | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module SubjectDecorator | |||
| def can_visited? | |||
| published? || User.current.admin? || member?(User.current) | |||
| end | |||
| end | |||
| @@ -1,5 +0,0 @@ | |||
| module VideoDecorator | |||
| extend ApplicationDecorator | |||
| display_time_method :published_at, :created_at, :updated_at | |||
| end | |||
| @@ -12,6 +12,7 @@ toc_footers: | |||
| includes: | |||
| - licenses | |||
| - gitignores | |||
| - public_keys | |||
| - users | |||
| - projects | |||
| - repositories | |||
| @@ -0,0 +1,158 @@ | |||
| <!-- | |||
| * @Date: 2021-07-14 15:10:29 | |||
| * @LastEditors: viletyy | |||
| * @LastEditTime: 2021-07-14 15:37:23 | |||
| * @FilePath: /forgeplus/app/docs/slate/source/includes/_public_keys.md | |||
| --> | |||
| # PublicKeys | |||
| ## public_keys列表 | |||
| 获取public_keys列表,支持分页 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/public_keys.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/public_keys.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET api/public_keys.json` | |||
| ### 请求参数 | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| page |否| 1 | int | 页码 | | |||
| limit |否| 15 | int | 每页数量 | | |||
| ### 返回字段说明 | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| total_count |int |总数 | | |||
| public_keys.id |int |ID| | |||
| public_keys.name |string|密钥标题| | |||
| public_keys.content |string|密钥内容| | |||
| public_keys.fingerprint |string|密钥标识| | |||
| public_keys.created_time |string|密钥创建时间| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "total_count": 1, | |||
| "public_keys": [ | |||
| { | |||
| "id": 16, | |||
| "name": "xxx", | |||
| "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", | |||
| "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM", | |||
| "created_unix": 1626246596, | |||
| "created_time": "2021/07/14 15:09" | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success — a happy kitten is an authenticated kitten! | |||
| </aside> | |||
| ## 创建public_key | |||
| 创建public_key | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST \ | |||
| http://localhost:3000/api/public_keys.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/public_keys.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST api/public_keys.json` | |||
| ### 请求参数 | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| key |是 | 否 | string | 密钥 | | |||
| title |是 | 否 | string | 密钥标题 | | |||
| > 请求的JSON示例: | |||
| ```json | |||
| { | |||
| "public_key": { | |||
| "key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", | |||
| "title": "xxx" | |||
| } | |||
| } | |||
| ``` | |||
| ### 返回字段说明 | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| total_count |int |总数 | | |||
| id |int |ID| | |||
| name |string|密钥标题| | |||
| content |string|密钥内容| | |||
| fingerprint |string|密钥标识| | |||
| created_time |string|密钥创建时间| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "id": 17, | |||
| "name": "xxx", | |||
| "content": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDe5ETOTB5PcmcYJkIhfF7+mxmJQDCLg7/LnMoKHpKoo/jYUnFU9OjfsxVo3FTNUvh2475WXMAur5KsFoNKjK9+JHxvoXyJKmyVPWgXU/NRxQyaWPnPLPK8qPRF5ksJE6feBOqtsdxsvBiHs2r1NX/U26Ecnpr6avudD0cmyrEfbYMWbupLrhsd39dswPT73f3W5jc7B9Y47Ioiv8UOju3ABt1+kpuAjaaVC6VtUQoEFiZb1y33yBnyePya7dvFyApyD4ILyyIG2rtZWK7l53YFnwZDuFsTWjEEEQD0U4FBSFdH5wtwx0WQLMSNyTtaFBSG0kJ+uiQQIrxlvikcm63df7zbC3/rWLPsKgW122Zt966dcpFqiCiJNDKZPPw3qpg8TBL6X+qIZ+FxVEk/16/zScpyEfoxQp0GvgxI7hPLErmfkC5tMsib8MAXYBNyvJXna0vg/wOaNNIaI4SAH9Ksh3f/TtalYVjp6WxIwVBfnbq51WnmlnEXePtX6XjAGL+GbF2VQ1nv/IzrY09tNbTV6wQsrSIP3VDzYQxdJ1rdsVNMoJB0H2Pu0NdcSz53Wx45N+myD0QnE05ss+zDp5StY90OYsx2aCo6qAA8Qn2jUjdta7MQWwkPfKrta4tTQ0XbWMjx4/E1+l3J5liwZkl2XOGOwhfXdRsBjaEziZ18kQ== yystopf@163.com", | |||
| "fingerprint": "SHA256:cU8AK/+roqUUyiaYXIdS2Nj4+Rb2p6rqWSeRDc+aqKM", | |||
| "created_time": "2021/07/14 15:26" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success — a happy kitten is an authenticated kitten! | |||
| </aside> | |||
| ## 删除public_key | |||
| 删除public_key | |||
| > 示例: | |||
| ```shell | |||
| curl -X DELETE \ | |||
| http://localhost:3000/api/public_keys/:id.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('DELETE /api/public_keys/:id.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `DELETE api/public_keys/:id.json` | |||
| ### 请求参数 | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| id |是 | 否 | int | 密钥ID | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success — a happy kitten is an authenticated kitten! | |||
| </aside> | |||
| @@ -1 +1,502 @@ | |||
| # Pulls | |||
| ## Get a pull request | |||
| 获取合并请求详情接口 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls/88.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/Jasder/gitlink/pulls/88.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/pulls/:id.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |id |是| | integer | pull id值 | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "响应成功", | |||
| "project_name": "Gitlink", | |||
| "identifier": "forgeplus", | |||
| "project_identifier": "forgeplus", | |||
| "pr_time": "52分钟前", | |||
| "commits_count": 229, | |||
| "files_count": 328, | |||
| "comments_count": 0, | |||
| "comments_total_count": 0, | |||
| "pull_request": { | |||
| "id": 1189, | |||
| "base": "master", | |||
| "head": "develop", | |||
| "status": 0, | |||
| "fork_project_id": null, | |||
| "is_original": false, | |||
| "pull_request_staus": "open", | |||
| "fork_project_user": null, | |||
| "create_user": "jasder", | |||
| "mergeable": true, | |||
| "state": "open" | |||
| }, | |||
| "issue": { | |||
| "id": 51888, | |||
| "subject": "FIx release v3.2.0", | |||
| "description": null, | |||
| "is_private": false, | |||
| "branch_name": null, | |||
| "project_author_name": "Gitlink", | |||
| "closed_on": "", | |||
| "created_at": "2021-10-12 15:51", | |||
| "assign_user_name": "victor", | |||
| "assign_user_login": "moshenglv", | |||
| "author_name": "段甲生", | |||
| "author_login": "jasder", | |||
| "author_picture": "images/avatars/User/36480?t=1615520120", | |||
| "issue_status": "新增", | |||
| "priority": "正常", | |||
| "version": null, | |||
| "issue_tags": null | |||
| }, | |||
| "conflict_files": [] | |||
| } | |||
| ``` | |||
| ## 获取pull request文件列表 | |||
| 获取pull request文件列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/Jason/gitlink/pulls/1/files.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/jasder/gitlink/pulls/1/files.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/pulls/:id/files.json` | |||
| ### 请求参数: | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |owner |是|string |用户登录名 | | |||
| |repo |是|string |project's identifier | | |||
| |id |是|int |pull request's id | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |-|-|-| | |||
| |files_count |int|文件更改的总数量| | |||
| |total_addition |int|添加代码总行数| | |||
| |total_deletion |int|删除代码总行数| | |||
| |files |array|| | |||
| |-- sha |string|commit's sha value| | |||
| |-- name |string|当前文件名| | |||
| |-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改| | |||
| |-- addition |int|文件添加的行数| | |||
| |-- deletion |int|文件删除的行数| | |||
| |-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容| | |||
| |-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否| | |||
| |-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否| | |||
| |-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否| | |||
| |-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否| | |||
| |-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否| | |||
| |-- sections |array|| | |||
| |---- fileName |string|文件名称| | |||
| |---- lines |array|| | |||
| |------ leftIdx |string|文件变动之前所在行数| | |||
| |------ rightIdx |string|文件更改后所在行数| | |||
| |------ type |string|文件变更类型,1: 新增,2: 修改, 3: 删除, 4: diff统计信息| | |||
| |------ content |string|文件变更的内容| | |||
| |------ sectionInfo |object|| | |||
| |-------- path |string|文件相对仓库的路径| | |||
| |-------- lastLeftIdx |int|| | |||
| |-------- lastRightIdx |int|| | |||
| |-------- leftHunkSize |int|文件变更之前的行数| | |||
| |-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)| | |||
| |-------- leftIdx |int|文件变更之前所在行数| | |||
| |-------- rightIdx |int|文件变更之后所在行数(即:页面编辑器开始显示的行数)| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "files_count": 6, | |||
| "total_addition": 447, | |||
| "total_deletion": 0, | |||
| "files": [ | |||
| { | |||
| "sha": "xefenisnii", | |||
| "name": "文件.txt", | |||
| "old_name": "文件.txt", | |||
| "index": 6, | |||
| "addition": 2, | |||
| "deletion": 0, | |||
| "type": 1, | |||
| "isCreated": true, | |||
| "isDeleted": false, | |||
| "isBin": false, | |||
| "isLFSFile": false, | |||
| "isRenamed": false, | |||
| "isSubmodule": false, | |||
| "sections": [ | |||
| { | |||
| "fileName": "文件.txt", | |||
| "name": "", | |||
| "lines": [ | |||
| { | |||
| "leftIdx": 0, | |||
| "rightIdx": 0, | |||
| "type": 4, | |||
| "content": "@@ -0,0 +1,2 @@", | |||
| "sectionInfo": { | |||
| "path": null, | |||
| "lastLeftIdx": null, | |||
| "lastRightIdx": null, | |||
| "leftIdx": 0, | |||
| "rightIdx": 0, | |||
| "leftHunkSize": null, | |||
| "rightHunkSize": null | |||
| } | |||
| }, | |||
| { | |||
| "leftIdx": 0, | |||
| "rightIdx": 1, | |||
| "type": 2, | |||
| "content": "+用例图一致性更新", | |||
| "sectionInfo": null | |||
| }, | |||
| { | |||
| "leftIdx": 0, | |||
| "rightIdx": 2, | |||
| "type": 2, | |||
| "content": "+工程文件直接上传会有文件缺失,现在压缩后上传", | |||
| "sectionInfo": null | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| ## 获取pull request的commits列表 | |||
| 获取pull request的commits列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/jasder/jasder_test/pulls/1/commits.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/jasder/jasder_test/pulls/1/commits.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/pulls/:id/commits.json` | |||
| ### 请求参数: | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |owner |是|string |用户登录名 | | |||
| |repo |是|string |project's identifier | | |||
| |id |是|int |pull request's id | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |commits_count |int|commits总数量| | |||
| |commits |array|| | |||
| |-- author |object|项目作者| | |||
| |---- login |string|用户login| | |||
| |---- name |string|用户姓名| | |||
| |---- image_url |string|用户头像| | |||
| |-- committer |object|commit提交用户| | |||
| |---- login |string|用户login| | |||
| |---- name |string|用户姓名| | |||
| |---- image_url |string|用户头像| | |||
| |-- timestamp |int|commit的unix时间戳| | |||
| |-- time_from_now|string|commit’s 提交时间距当前时间的时间值| | |||
| |-- message |string|commit说明信息| | |||
| |-- sha |string|commit’s sha值| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "commits_count": 1, | |||
| "commits": [ | |||
| { | |||
| "author": { | |||
| "id": 36480, | |||
| "login": "jasder", | |||
| "name": "段甲生", | |||
| "image_url": "avatars/User/b" | |||
| }, | |||
| "committer": { | |||
| "id": 36480, | |||
| "login": "jasder", | |||
| "name": "段甲生", | |||
| "image_url": "avatars/User/b" | |||
| }, | |||
| "timestamp": 1604382982, | |||
| "time_from_now": "3小时前", | |||
| "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n", | |||
| "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968" | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| ## Compare two commits | |||
| Compare two commits | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/Jason/test-txt/compare/master...develop | |||
| curl -X GET \ | |||
| http://localhost:3000/api/Jason/test-txt/compare/master...Jason/test-txt:develop | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/Jason/test-txt/compare/master...Jason/test-txt:develop') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/compare/{base}...{head}.json` | |||
| ### 请求参数: | |||
| |参数名|必选|类型|说明| | |||
| |-|-|-|-| | |||
| |owner |是|string |用户登录名 | | |||
| |repo |是|string |project's identifier | | |||
| |base |是|string |pull request's id | | |||
| |head |是|string |pull request's id | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |-|-|-| | |||
| |commits_count |int|commits总数量| | |||
| |commits |array|| | |||
| |-- author |object|项目作者| | |||
| |---- login |string|用户login| | |||
| |---- name |string|用户姓名| | |||
| |---- image_url |string|用户头像| | |||
| |-- committer |object|commit提交用户| | |||
| |---- login |string|用户login| | |||
| |---- name |string|用户姓名| | |||
| |---- image_url |string|用户头像| | |||
| |-- timestamp |int|commit的unix时间戳| | |||
| |-- time_from_now|string|commit’s 提交时间距当前时间的时间值| | |||
| |-- message |string|commit说明信息| | |||
| |-- sha |string|commit’s sha值| | |||
| |diff |object|| | |||
| |-- files_count |int|文件更改的总数量| | |||
| |-- total_addition |int|添加代码总行数| | |||
| |-- total_deletion |int|删除代码总行数| | |||
| |-- files |Array|| | |||
| |-- sha |string|commit's sha | | |||
| |-- name |string|当前文件名| | |||
| |-- old_name |string| 修改之前的文件名称,与name相同的话,说明文件名未更改| | |||
| |-- addition |int|文件添加的行数| | |||
| |-- deletion |int|文件删除的行数| | |||
| |-- type |int|文件类型, 1: 表示该文件只添加了内容,2: 表示该文件内容有修改, 3: 表示文件被删除或者改文件只删除了内容| | |||
| |-- isCreated |boolean|当前文件是否为新增文件, true: 是, false: 否| | |||
| |-- isDeleted |boolean|当前文件是否被删除, true: 是,false: 否| | |||
| |-- isBin |boolean|当前文件是否为二进制文件,true: 是,false: 否| | |||
| |-- isLFSFile |boolean|当前文件是否为LFS文件,true: 是,false: 否| | |||
| |-- isRenamed |boolean|当前文件是否被重命名,true: 是,false: 否| | |||
| |-- sections |array|| | |||
| |---- fileName |string|文件名称| | |||
| |---- lines |array|| | |||
| |------ leftIdx |string|文件变动之前所在行数| | |||
| |------ rightIdx |string|文件更改后所在行数| | |||
| |------ type |string|文件变更类型,1: 内容未改动,2: 添加, 3: 删除, 4: diff统计信息| | |||
| |------ content |string|文件变更的内容| | |||
| |------ sectionInfo |object|| | |||
| |-------- path |string|文件相对仓库的路径| | |||
| |-------- lastLeftIdx |int|| | |||
| |-------- lastRightIdx |int|| | |||
| |-------- leftHunkSize |int|文件变更之前的行数| | |||
| |-------- rightHunkSize |int|文件变更之后的行数(及当前页面编辑器显示的总行数)| | |||
| |-------- leftIdx |int|文件变更之前所在行数| | |||
| |-------- rightIdx |int|文件变更之后所在行数| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "commits_count": 1, | |||
| "commits": [ | |||
| { | |||
| "author": { | |||
| "id": 36480, | |||
| "login": "jasder", | |||
| "name": "段甲生", | |||
| "image_url": "avatars/User/b" | |||
| }, | |||
| "committer": { | |||
| "id": 36480, | |||
| "login": "jasder", | |||
| "name": "段甲生", | |||
| "image_url": "avatars/User/b" | |||
| }, | |||
| "timestamp": 1604382982, | |||
| "time_from_now": "4小时前", | |||
| "message": "add some file\n* Add the tag list page to the release page\n* Apply suggestions from code review\n* Add the tags list view\n* Add the delete tag way on ui\n* Not delete tag and clear message when delete a release\n", | |||
| "sha": "8f5faee0d3b3be1b8063e84da0c79dd75327b968" | |||
| } | |||
| ], | |||
| "diff": { | |||
| "files_count": 6, | |||
| "total_addition": 447, | |||
| "total_deletion": 0, | |||
| "files": [ | |||
| { | |||
| "name": "build.go", | |||
| "old_name": "build.go", | |||
| "index": 1, | |||
| "addition": 33, | |||
| "deletion": 0, | |||
| "type": 1, | |||
| "isCreated": true, | |||
| "isDeleted": false, | |||
| "isBin": false, | |||
| "isLFSFile": false, | |||
| "isRenamed": false, | |||
| "isSubmodule": false, | |||
| "sections": [ | |||
| { | |||
| "fileName": "build.go", | |||
| "name": "", | |||
| "lines": [ | |||
| { | |||
| "leftIdx": 0, | |||
| "rightIdx": 0, | |||
| "type": 4, | |||
| "content": "@@ -0,0 +1,33 @@", | |||
| "sectionInfo": { | |||
| "path": "build.go", | |||
| "lastLeftIdx": 0, | |||
| "lastRightIdx": 0, | |||
| "leftIdx": 0, | |||
| "rightIdx": 1, | |||
| "leftHunkSize": 0, | |||
| "rightHunkSize": 33 | |||
| } | |||
| }, | |||
| { | |||
| "leftIdx": 0, | |||
| "rightIdx": 1, | |||
| "type": 2, | |||
| "content": "+// Copyright 2020 The Gitea Authors. All rights reserved.", | |||
| "sectionInfo": null | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| ## List pull requests | |||
| 获取合并请求列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/Jasder/gitlink/pulls.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/Jasder/gitlink/pulls.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/pulls.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "响应成功", | |||
| "open_count": 4, | |||
| "close_count": 51, | |||
| "merged_issues_size": 123, | |||
| "search_count": 4, | |||
| "limit": null, | |||
| "user_admin_or_member": true, | |||
| "user_admin_or_developer": true, | |||
| "project_name": "Gitlink", | |||
| "project_author_name": "Gitlink", | |||
| "issues": [ | |||
| { | |||
| "pull_request_id": 1189, | |||
| "pull_request_status": 0, | |||
| "pull_request_head": "develop", | |||
| "pull_request_base": "master", | |||
| "pull_request_staus": "open", | |||
| "is_original": false, | |||
| "fork_project_id": null, | |||
| "fork_project_identifier": null, | |||
| "fork_project_user": null, | |||
| "id": 51888, | |||
| "name": "FIx release v3.2.0", | |||
| "pr_time": "59分钟前", | |||
| "assign_user_name": "victor", | |||
| "assign_user_login": "moshenglv", | |||
| "author_name": "段甲生", | |||
| "author_login": "jasder", | |||
| "avatar_url": "images/avatars/User/36480?t=1615520120", | |||
| "priority": "正常", | |||
| "version": null, | |||
| "journals_count": 0, | |||
| "issue_tags": null | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| @@ -274,6 +274,124 @@ await octokit.request('GET /api/yystopf/ceshi/detail.json') | |||
| } | |||
| ``` | |||
| ## 仓库标签列表 | |||
| 仓库标签列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/yystopf/csfjkkj/tags.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/csfjkkj/tags.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/tags.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |page |否| 1 | integer | 页码 | | |||
| |limit |否| 20| integer | 每页个数 | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |id |int |标签id | | |||
| |name |string|标签名称| | |||
| |zipball_url |string|标签zip包下载地址| | |||
| |tarball_url |string|标签tar包下载地址| | |||
| |tagger |object|打标签的人| | |||
| |time_ago |string|打标签的时间| | |||
| |created_at_unix|string|打标签的时间戳| | |||
| |message |string|标签信息| | |||
| |commit |object|标签最后一个commit| | |||
| |commit.sha |string|commit的id| | |||
| |commit.message |string|commit的提交信息| | |||
| |commit.time_ago|string|commit的提交时间| | |||
| |commit.created_at_unix|string|commit的提交时间戳| | |||
| |commit.committer|object|commit的提交者| | |||
| |commit.author|object|commit的作者| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| [ | |||
| { | |||
| "name": "v2.0.0", | |||
| "id": "c7d0873ee41796d1a0e193063095ccf539a9bf31", | |||
| "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.zip", | |||
| "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v2.0.0.tar.gz", | |||
| "tagger": { | |||
| "id": 4, | |||
| "login": "testforge1", | |||
| "name": "testforge1", | |||
| "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" | |||
| }, | |||
| "time_ago": "1天前", | |||
| "created_at_unix": 1632376903, | |||
| "message": "jdfkls", | |||
| "commit": { | |||
| "sha": "08fe383f1e5ebe2e2a384a8ea3ee890a758c7cd7", | |||
| "message": "add\n", | |||
| "time_ago": "1天前", | |||
| "created_at_unix": 1632376186, | |||
| "committer": { | |||
| "id": 4, | |||
| "login": "testforge1", | |||
| "name": "testforge1", | |||
| "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" | |||
| }, | |||
| "author": { | |||
| "id": 4, | |||
| "login": "testforge1", | |||
| "name": "testforge1", | |||
| "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" | |||
| } | |||
| } | |||
| }, | |||
| { | |||
| "name": "v1.0.0", | |||
| "id": "12168ad39c3ef201a445a2db181a3e43d50e40dd", | |||
| "zipball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.zip", | |||
| "tarball_url": "http://localhost:3000/api/yystopf/csfjkkj/archive/v1.0.0.tar.gz", | |||
| "tagger": { | |||
| "id": null, | |||
| "login": "viletyy", | |||
| "name": "viletyy", | |||
| "image_url": "system/lets/letter_avatars/2/V/39_141_222/120.png" | |||
| }, | |||
| "time_ago": "10天前", | |||
| "created_at_unix": 1631588042, | |||
| "message": "dfks", | |||
| "commit": { | |||
| "sha": "5291b5e45a377c1f7710cc6647259887ed7aaccf", | |||
| "message": "ADD file via upload\n", | |||
| "time_ago": "21天前", | |||
| "created_at_unix": 1630648417, | |||
| "committer": { | |||
| "id": null, | |||
| "login": "yystopf", | |||
| "name": "yystopf", | |||
| "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" | |||
| }, | |||
| "author": { | |||
| "id": null, | |||
| "login": "yystopf", | |||
| "name": "yystopf", | |||
| "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" | |||
| } | |||
| } | |||
| } | |||
| ] | |||
| ``` | |||
| ## 编辑仓库信息 | |||
| 编辑仓库信息 | |||
| @@ -867,3 +985,796 @@ await octokit.request('GET /api/jasder/jasder_test/sub_entries.json') | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取仓库README文件 | |||
| 获取仓库README文件 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| -d "ref=master" \ | |||
| -d "filepath=lib" \ | |||
| http://localhost:3000/api/yystopf/csfjkkj/readme.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/csfjkkj/readme.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/readme.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |ref |否| | string |分支名称、tag名称或是提交记录id,默认为默认分支 | | |||
| |filepath |否| | string |子目录名称,默认为空 | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type |string|文件类型, file:文件,dir:文件目录 | |||
| |encoding |string |编码 | | |||
| |size |int|文件夹或文件大小 单位B | |||
| |name |string|文件夹或文件名称| | |||
| |path |string|文件夹或文件相对路径| | |||
| |content |string|文件内容 | |||
| |sha |string|文件commitid | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "type": "file", | |||
| "encoding": "base64", | |||
| "size": 24, | |||
| "name": "README.md", | |||
| "path": "lib/README.md", | |||
| "content": "ZGZhc2RhZGpmIGRrZnNsCgpzZGZkZnMK", | |||
| "sha": "860962cd21c60b1a9e07d723080c87c32c18d44a" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取仓库贡献者 | |||
| 获取仓库贡献者 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| -d "ref=master" \ | |||
| -d "filepath=lib" \ | |||
| http://localhost:3000/api/yystopf/csfjkkj/contributors.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/csfjkkj/contributors.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/contributors.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |ref |否| | string |分支名称、tag名称或是提交记录id,默认为整个仓库 | | |||
| |filepath |否| | string |子目录名称,默认为空 | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |total_count |integer|贡献者数量| | |||
| |contributions |integer|贡献数量| | |||
| |login |string |用户登录名 | | |||
| |type |string|用户类型 | | |||
| |name |string|用户昵称| | |||
| |image_url |string|用户头像| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "contributors": [ | |||
| { | |||
| "contributions": 5, | |||
| "login": "testforge2", | |||
| "type": "User", | |||
| "name": "testforge2", | |||
| "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png" | |||
| }, | |||
| { | |||
| "contributions": 79, | |||
| "login": "yystopf", | |||
| "type": "User", | |||
| "name": "yystopf", | |||
| "image_url": "system/lets/letter_avatars/2/Y/241_125_89/120.png" | |||
| } | |||
| ], | |||
| "total_count": 2 | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取仓库webhooks列表 | |||
| 获取仓库webhooks列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/ceshi/webhooks.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/webhooks.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |id |int |id | | |||
| |url |string|地址| | |||
| |http_method |string|请求方式| | |||
| |is_active |bool |是否激活| | |||
| |type |string|类型| | |||
| |last_status |string|最后一次推送的状态| | |||
| |create_time |string|创建时间| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "total_count": 4, | |||
| "webhooks": [ | |||
| { | |||
| "id": 2, | |||
| "url": "https://oapi.dingtalk.com/robot/send?access_token=7e1e19d0eddb6a5e33c5c2c4e66f4c88f9437184b9ed2c2653194c6374c7d513", | |||
| "http_method": "", | |||
| "is_active": true, | |||
| "type": "dingtalk", | |||
| "last_status": "succeed", | |||
| "create_time": "2021-07-12 10:50:07" | |||
| }, | |||
| { | |||
| "id": 3, | |||
| "url": "http://localhost:3000", | |||
| "http_method": "GET", | |||
| "is_active": true, | |||
| "type": "gitea", | |||
| "last_status": "succeed", | |||
| "create_time": "2021-07-26 10:03:45" | |||
| }, | |||
| { | |||
| "id": 4, | |||
| "url": "http://localhost:10081", | |||
| "http_method": "POST", | |||
| "is_active": true, | |||
| "type": "gitea", | |||
| "last_status": "waiting", | |||
| "create_time": "2021-07-26 16:56:53" | |||
| }, | |||
| { | |||
| "id": 5, | |||
| "url": "http://localhost:3001", | |||
| "http_method": "POST", | |||
| "is_active": true, | |||
| "type": "gitea", | |||
| "last_status": "fail", | |||
| "create_time": "2021-07-26 16:58:23" | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取仓库单个webhook | |||
| 获取仓库单个webhook | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks/3/edit.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/ceshi/webhooks/3/edit.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/webhooks/:id/edit.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |id |是||integer|webhook ID| | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |id |int |id | | |||
| |url |string|地址| | |||
| |content_type |string|POST Content Type| | |||
| |http_method |string|请求方式| | |||
| |secret| |string|密钥| | |||
| |is_active |bool |是否激活| | |||
| |type |string|类型| | |||
| |last_status |string|最后一次推送的状态, waiting 等待,fail 失败,succeed 成功| | |||
| |branch_filter |string|分支过滤| | |||
| |events |string|触发条件| | |||
| |create_time |string|创建时间| | |||
| 参数| 含义| | |||
| --------- | ------- | ------- | | |||
| |create|创建分支或标签| | |||
| |delete|分支或标签删除| | |||
| |fork|仓库被fork| | |||
| |push|git仓库推送| | |||
| |issue|易修已打开、已关闭、已重新打开或编辑| | |||
| |issue_assign|易修被指派| | |||
| |issue_label|易修标签被更新或删除| | |||
| |issue_milestone|易修被收入里程碑| | |||
| |issue_comment|易修评论| | |||
| |pull_request|合并请求| | |||
| |pull_request_assign|合并请求被指派| | |||
| |pull_request_label|合并请求被贴上标签| | |||
| |pull_request_milestone|合并请求被记录于里程碑中| | |||
| |pull_request_comment|合并请求被评论| | |||
| |pull_request_review_approved|合并请求被批准| | |||
| |pull_request_review_rejected|合并请求被拒绝| | |||
| |pull_request_review_comment|合并请求被提出审查意见| | |||
| |pull_request_sync|合并请求被同步| | |||
| |repository|创建或删除仓库| | |||
| |release|版本发布| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "id": 3, | |||
| "http_method": "GET", | |||
| "content_type": "form", | |||
| "url": "http://localhost:3000", | |||
| "secret": "123456", | |||
| "last_status": "succeed", | |||
| "is_active": true, | |||
| "type": "gitea", | |||
| "create_time": "2021-07-26 10:03:45", | |||
| "branch_filter": "*", | |||
| "events": [ | |||
| "create", | |||
| "delete", | |||
| "fork", | |||
| "issues", | |||
| "issue_assign", | |||
| "issue_label", | |||
| "issue_milestone", | |||
| "issue_comment", | |||
| "push", | |||
| "pull_request", | |||
| "pull_request_assign", | |||
| "pull_request_label", | |||
| "pull_request_milestone", | |||
| "pull_request_comment", | |||
| "pull_request_review", | |||
| "pull_request_sync", | |||
| "repository", | |||
| "release" | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 添加仓库webhook | |||
| 添加仓库webhook | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/yystopf/ceshi/webhooks.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST /api/:owner/:repo/webhooks.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| | string |用户登录名 | | |||
| |repo |是| | string |项目标识identifier | | |||
| |webhook.url |是| | string |目标url | | |||
| |webhook.type |否| | string |类型| | |||
| |webhook.http_method |是| | string | http方法, POST和GET | | |||
| |webhook.content_type |是| | string | POST Content Type | | |||
| |webhook.secret |否| | string |密钥文本| | |||
| |webhook.active |是| | bool | 是否激活| | |||
| |webhook.branch_filter|否| |string|分支过滤| | |||
| |webhook.events |否| |array|触发事件| | |||
| 触发事件字段说明 | |||
| 参数| 含义| | |||
| --------- | ------- | ------- | | |||
| |create|创建分支或标签| | |||
| |delete|分支或标签删除| | |||
| |fork|仓库被fork| | |||
| |push|git仓库推送| | |||
| |issue|易修已打开、已关闭、已重新打开或编辑| | |||
| |issue_assign|易修被指派| | |||
| |issue_label|易修标签被更新或删除| | |||
| |issue_milestone|易修被收入里程碑| | |||
| |issue_comment|易修评论| | |||
| |pull_request|合并请求| | |||
| |pull_request_assign|合并请求被指派| | |||
| |pull_request_label|合并请求被贴上标签| | |||
| |pull_request_milestone|合并请求被记录于里程碑中| | |||
| |pull_request_comment|合并请求被评论| | |||
| |pull_request_review_approved|合并请求被批准| | |||
| |pull_request_review_rejected|合并请求被拒绝| | |||
| |pull_request_review_comment|合并请求被提出审查意见| | |||
| |pull_request_sync|合并请求被同步| | |||
| |repository|创建或删除仓库| | |||
| |release|版本发布| | |||
| > 请求的JSON示例: | |||
| ```json | |||
| { | |||
| "active": true, | |||
| "content_type": "json", | |||
| "http_method": "GET", | |||
| "secret": "123456", | |||
| "url": "http://localhost:10000", | |||
| "branch_filter": "*", | |||
| "events": ["push"] | |||
| } | |||
| ``` | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |id |int |id | | |||
| |url |string|地址| | |||
| |content_type |string|POST Content Type| | |||
| |is_active |bool |是否激活| | |||
| |type |string|类型| | |||
| |events | array|触发事件 | | |||
| |create_time |string|创建时间| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "id": 18, | |||
| "type": "gitea", | |||
| "content_type": "json", | |||
| "url": "http://localhost:10000", | |||
| "events": [ | |||
| "push" | |||
| ], | |||
| "active": true, | |||
| "create_time": "2021-07-26 18:53:43" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 更新仓库webhook | |||
| 更新仓库webhook | |||
| > 示例: | |||
| ```shell | |||
| curl -X PATCH \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks/7.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('PATCH /api/yystopf/ceshi/webhooks/7.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `PATCH /api/:owner/:repo/webhooks/:id.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| | string |用户登录名 | | |||
| |repo |是| | string |项目标识identifier | | |||
| |id |是| | string |webhook id | | |||
| |webhook.url |是| | string |目标url | | |||
| |webhook.type |否| | string |类型| | |||
| |webhook.http_method |是| | string | http方法, POST和GET | | |||
| |webhook.content_type |是| | string | POST Content Type | | |||
| |webhook.secret |否| | string |密钥文本| | |||
| |webhook.active |是| | bool | 是否激活| | |||
| |webhook.branch_filter|否| |string|分支过滤| | |||
| |webhook.events |否| |array|触发事件| | |||
| 触发事件字段说明 | |||
| 参数| 含义| | |||
| --------- | ------- | ------- | | |||
| |create|创建分支或标签| | |||
| |delete|分支或标签删除| | |||
| |fork|仓库被fork| | |||
| |push|git仓库推送| | |||
| |issue|易修已打开、已关闭、已重新打开或编辑| | |||
| |issue_assign|易修被指派| | |||
| |issue_label|易修标签被更新或删除| | |||
| |issue_milestone|易修被收入里程碑| | |||
| |issue_comment|易修评论| | |||
| |pull_request|合并请求| | |||
| |pull_request_assign|合并请求被指派| | |||
| |pull_request_label|合并请求被贴上标签| | |||
| |pull_request_milestone|合并请求被记录于里程碑中| | |||
| |pull_request_comment|合并请求被评论| | |||
| |pull_request_review_approved|合并请求被批准| | |||
| |pull_request_review_rejected|合并请求被拒绝| | |||
| |pull_request_review_comment|合并请求被提出审查意见| | |||
| |pull_request_sync|合并请求被同步| | |||
| |repository|创建或删除仓库| | |||
| |release|版本发布| | |||
| > 请求的JSON示例: | |||
| ```json | |||
| { | |||
| "active": true, | |||
| "content_type": "json", | |||
| "http_method": "GET", | |||
| "secret": "123456", | |||
| "url": "http://localhost:10000", | |||
| "branch_filter": "*", | |||
| "events": ["push"] | |||
| } | |||
| ``` | |||
| ### 返回字段说明: | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 删除仓库webhook | |||
| 删除仓库webhook | |||
| > 示例: | |||
| ```shell | |||
| curl -X DELETE \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks/7.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('DELETE /api/yystopf/ceshi/webhooks/7.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `DELETE /api/:owner/:repo/webhooks/:id.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| | string |用户登录名 | | |||
| |repo |是| | string |项目标识identifier | | |||
| |id |是| | string |webhook id | | |||
| ### 返回字段说明: | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取仓库webhook的历史推送列表 | |||
| 获取仓库webhook的历史推送列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks/3/tasks.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/yystopf/ceshi/webhooks/3/tasks.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/:owner/:repo/webhooks/:id/tasks.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| |string |用户登录名 | | |||
| |repo |是| |string |项目标识identifier | | |||
| |id |是| |integer |webhook ID| | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |id |int |id | | |||
| |uuid |string|推送uuid| | |||
| |type |string|类型| | |||
| |is_succeed |bool|是否推送成功| | |||
| |is_delivered |bool|是否完成推送| | |||
| |payload_content |json|请求主体内容| | |||
| |request_content |json|请求内容,头部等等| | |||
| |reponse_content |json|响应内容,状态,头部,主体等等| | |||
| |delivered_time |string|推送时间| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "total_count": 6, | |||
| "tasks": [ | |||
| { | |||
| "id": 20, | |||
| "type": "gitea", | |||
| "uuid": "99aa2c23-6884-4c44-9020-5469320aa408", | |||
| "is_succeed": true, | |||
| "is_delivered": true, | |||
| "payload_content": { | |||
| "secret": "123456", | |||
| "ref": "refs/heads/master", | |||
| "before": "feb48e31362787a7620b53d4df3c4effddbb6f0b", | |||
| "after": "feb48e31362787a7620b53d4df3c4effddbb6f0b", | |||
| "compare_url": "", | |||
| "commits": [ | |||
| { | |||
| "id": "feb48e31362787a7620b53d4df3c4effddbb6f0b", | |||
| "message": "fix\n", | |||
| "url": "http://localhost:10081/yystopf/ceshi/commit/feb48e31362787a7620b53d4df3c4effddbb6f0b", | |||
| "author": { | |||
| "name": "viletyy", | |||
| "email": "yystopf@163.com", | |||
| "username": "root" | |||
| }, | |||
| "committer": { | |||
| "name": "viletyy", | |||
| "email": "yystopf@163.com", | |||
| "username": "root" | |||
| }, | |||
| "verification": { | |||
| "verified": false, | |||
| "reason": "gpg.error.not_signed_commit", | |||
| "signature": "", | |||
| "signer": null, | |||
| "payload": "" | |||
| }, | |||
| "timestamp": "2021-07-26T13:52:13+08:00", | |||
| "added": null, | |||
| "removed": null, | |||
| "modified": null | |||
| } | |||
| ], | |||
| "head_commit": null, | |||
| "repository": { | |||
| "id": 2, | |||
| "owner": { | |||
| "id": 3, | |||
| "login": "yystopf", | |||
| "full_name": "", | |||
| "email": "yystopf@forge.com", | |||
| "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", | |||
| "language": "zh-CN", | |||
| "is_admin": true, | |||
| "last_login": "2021-07-21T18:38:21+08:00", | |||
| "created": "2021-06-03T14:50:25+08:00", | |||
| "username": "yystopf" | |||
| }, | |||
| "name": "ceshi", | |||
| "full_name": "yystopf/ceshi", | |||
| "description": "", | |||
| "empty": false, | |||
| "private": false, | |||
| "fork": false, | |||
| "template": false, | |||
| "parent": null, | |||
| "mirror": false, | |||
| "size": 3846, | |||
| "html_url": "http://localhost:10081/yystopf/ceshi", | |||
| "ssh_url": "virus@localhost:10081:yystopf/ceshi.git", | |||
| "clone_url": "http://localhost:10081/yystopf/ceshi.git", | |||
| "original_url": "", | |||
| "website": "", | |||
| "stars_count": 0, | |||
| "forks_count": 1, | |||
| "watchers_count": 1, | |||
| "open_issues_count": 0, | |||
| "open_pr_counter": 0, | |||
| "release_counter": 0, | |||
| "default_branch": "master", | |||
| "archived": false, | |||
| "created_at": "2021-06-03T15:15:30+08:00", | |||
| "updated_at": "2021-07-26T13:52:16+08:00", | |||
| "permissions": { | |||
| "admin": false, | |||
| "push": false, | |||
| "pull": false | |||
| }, | |||
| "has_issues": true, | |||
| "internal_tracker": { | |||
| "enable_time_tracker": true, | |||
| "allow_only_contributors_to_track_time": true, | |||
| "enable_issue_dependencies": true | |||
| }, | |||
| "has_wiki": true, | |||
| "has_pull_requests": true, | |||
| "ignore_whitespace_conflicts": false, | |||
| "allow_merge_commits": true, | |||
| "allow_rebase": true, | |||
| "allow_rebase_explicit": true, | |||
| "allow_squash_merge": true, | |||
| "avatar_url": "", | |||
| "internal": false | |||
| }, | |||
| "pusher": { | |||
| "id": 0, | |||
| "login": "yystopf", | |||
| "full_name": "", | |||
| "email": "yystopf@forge.com", | |||
| "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", | |||
| "language": "", | |||
| "is_admin": false, | |||
| "last_login": "0001-01-01T00:00:00Z", | |||
| "created": "2021-06-03T14:50:25+08:00", | |||
| "username": "yystopf" | |||
| }, | |||
| "sender": { | |||
| "id": 0, | |||
| "login": "yystopf", | |||
| "full_name": "", | |||
| "email": "yystopf@forge.com", | |||
| "avatar_url": "http://localhost:10081/user/avatar/yystopf/-1", | |||
| "language": "", | |||
| "is_admin": false, | |||
| "last_login": "0001-01-01T00:00:00Z", | |||
| "created": "2021-06-03T14:50:25+08:00", | |||
| "username": "yystopf" | |||
| } | |||
| }, | |||
| "request_content": { | |||
| "headers": { | |||
| "X-GitHub-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", | |||
| "X-GitHub-Event": "push", | |||
| "X-Gitea-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", | |||
| "X-Gitea-Event": "push", | |||
| "X-Gitea-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66", | |||
| "X-Gogs-Delivery": "99aa2c23-6884-4c44-9020-5469320aa408", | |||
| "X-Gogs-Event": "push", | |||
| "X-Gogs-Signature": "34a01edcd952ff6410ff6ebc946471161bde74aff86171f21621d2c2c4130f66" | |||
| } | |||
| }, | |||
| "response_content": { | |||
| "status": 200, | |||
| "headers": { | |||
| "Cache-Control": "no-store, must-revalidate, private, max-age=0", | |||
| "Content-Length": "2556", | |||
| "Content-Type": "text/html; charset=utf-8", | |||
| "Referrer-Policy": "strict-origin-when-cross-origin", | |||
| "Set-Cookie": "__profilin=p%3Dt; path=/; HttpOnly", | |||
| "Vary": "Origin", | |||
| "X-Content-Type-Options": "nosniff", | |||
| "X-Download-Options": "noopen", | |||
| "X-Frame-Options": "SAMEORIGIN", | |||
| "X-Miniprofiler-Ids": "9ynvpncz5xm0rpgorb5y,hgggd9mv6lr4a9drcrlr,j7zqlx2vy5aji2vtgoba,f1ktsmh3jxvq0z2hf612,mih3dvgvlqhi3zy8lf2x,5k1qbkvbnru8mye9cest,tj6ern8w6awqf2zsimbr,9isaehvubivd52wo5p9v,1rzfhtq1nhuwbgy9p76g,z0xzidzyywna0y7a69m0,hzoklky92ycjqt42gi0s,y0ai7y0t28mcn8x0py2x,322il7nadinp51mw2r5m,m6dukftfsh6tjcxzp1gq,667wlqbytfwbrirnmma1,jcehj3dl8lkw8gk510cr", | |||
| "X-Miniprofiler-Original-Cache-Control": "max-age=0, private, must-revalidate", | |||
| "X-Permitted-Cross-Domain-Policies": "none", | |||
| "X-Request-Id": "08bff080-bbb5-4183-b845-81de3d47120a", | |||
| "X-Runtime": "0.394766", | |||
| "X-Xss-Protection": "1; mode=block" | |||
| }, | |||
| "body": "<!doctype html><html lang=\"zh-CN\" class=\"notranslate translated-ltr\" translate=\"no\"><head><meta charset=\"utf-8\"><meta name=\"”Keywords”\" content=\"”trustie,trustieforge,forge,确实让创建更美好,协同开发平台″\"><meta name=\"”Keywords”\" content=\"”TrustieOpenSourceProject″\"><meta name=\"”Keywords”\" content=\"”issue,bug,tracker,软件工程,课程实践″\"><meta name=\"”Description”\" content=\"”持续构建协同、共享、可信的软件创建生态开源创作与软件生产相结合,支持大规模群体开展软件协同创新活动”\"><meta name=\"theme-color\" content=\"#000000\"><link rel=\"manifest\" href=\"/react/build//manifest.json\"><link rel=\"stylesheet\" href=\"/react/build/css/iconfont.css\"><link rel=\"stylesheet\" href=\"/react/build/css/edu-purge.css\"><link rel=\"stylesheet\" href=\"/react/build/css/editormd.min.css\"><link rel=\"stylesheet\" href=\"/react/build/css/merge.css\"><link href=\"/react/build/static/css/main.07f7e90c.chunk.css\" rel=\"stylesheet\"></head><body><div id=\"md_div\" style=\"display:none\"></div><div id=\"root\" class=\"page -layout-v -fit widthunit\"></div><div id=\"picture_display\" style=\"display:none\"></div><script src=\"/react/build/js/jquery-1.8.3.min.js\"></script><script src=\"/react/build/js/js_min_all.js\"></script><script src=\"/react/build/js/codemirror/codemirror.js\"></script><script src=\"/react/build/js/editormd/editormd.min.js\"></script><script src=\"/react/build/js/codemirror/merge/merge.js\"></script><script src=\"/react/build/./static/js/runtime~main.3d644966.js\"></script><script src=\"/react/build/./static/js/main.e46872e3.chunk.js\"></script><script async type=\"text/javascript\" id=\"mini-profiler\" src=\"/mini-profiler-resources/includes.js?v=67dd1c2571ced7fc74ae7f1813e47bdf\" data-version=\"67dd1c2571ced7fc74ae7f1813e47bdf\" data-path=\"/mini-profiler-resources/\" data-current-id=\"9ynvpncz5xm0rpgorb5y\" data-ids=\"9ynvpncz5xm0rpgorb5y,hgggd9mv6lr4a9drcrlr,j7zqlx2vy5aji2vtgoba,f1ktsmh3jxvq0z2hf612,mih3dvgvlqhi3zy8lf2x,5k1qbkvbnru8mye9cest,tj6ern8w6awqf2zsimbr,9isaehvubivd52wo5p9v,1rzfhtq1nhuwbgy9p76g,z0xzidzyywna0y7a69m0,hzoklky92ycjqt42gi0s,y0ai7y0t28mcn8x0py2x,322il7nadinp51mw2r5m,m6dukftfsh6tjcxzp1gq,667wlqbytfwbrirnmma1,jcehj3dl8lkw8gk510cr\" data-horizontal-position=\"left\" data-vertical-position=\"top\" data-trivial=\"false\" data-children=\"false\" data-max-traces=\"20\" data-controls=\"false\" data-total-sql-count=\"false\" data-authorized=\"true\" data-toggle-shortcut=\"alt+p\" data-start-hidden=\"false\" data-collapse-results=\"true\" data-html-container=\"body\"></script>\n</body></html>" | |||
| }, | |||
| "delivered_time": "2021-07-28 11:47:29" | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 仓库webhook测试推送 | |||
| 仓库webhook测试推送 | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST \ | |||
| http://localhost:3000/api/yystopf/ceshi/webhooks/3/test.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/yystopf/ceshi/webhooks/3/test.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST /api/:owner/:repo/webhooks/:id/test.json` | |||
| ### 请求参数: | |||
| 参数 | 必选 | 默认 | 类型 | 字段说明 | |||
| --------- | ------- | ------- | -------- | ---------- | |||
| |owner |是| | string |用户登录名 | | |||
| |repo |是| | string |项目标识identifier | | |||
| |id |是| | integer|webhook ID| | |||
| ### 返回字段说明: | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| @@ -1,7 +1,7 @@ | |||
| <!-- | |||
| * @Date: 2021-03-01 10:35:21 | |||
| * @LastEditors: viletyy | |||
| * @LastEditTime: 2021-06-11 16:28:51 | |||
| * @LastEditTime: 2021-09-15 18:00:10 | |||
| * @FilePath: /forgeplus/app/docs/slate/source/includes/_users.md | |||
| --> | |||
| # Users | |||
| @@ -47,6 +47,305 @@ await octokit.request('GET /api/users/me.json') | |||
| Success Data. | |||
| </aside> | |||
| ## 用户消息列表 | |||
| 获取用户消息列表 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/users/:login/messages.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/users/:login/messages.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET api/users/yystopf/messages.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息| | |||
| |status | integer | 是否已读,不传为所有消息,1为未读,2为已读 | | |||
| |limit | integer | 每页个数 | | |||
| |page | integer | 页码 | | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |total_count | integer | 消息总数 | | |||
| |type | string | 消息类型 | | |||
| |unread_notification | integer | 未读系统通知数量 | | |||
| |unread_atme | integer | 未读@我数量 | | |||
| |messages.id | integer | 消息id | | |||
| |messages.status | integer | 消息是否已读,1为未读,2为已读 | | |||
| |messages.content | string | 消息内容 | | |||
| |messages.notification_url | string | 消息跳转地址 | | |||
| |messages.source | string | 消息来源 | | |||
| |messages.timeago | string | 消息时间 | | |||
| |messages.type | string | 消息类型,notification为系统消息,atme为@我消息| | |||
| |sender | object | 消息发送者 | | |||
| #### 消息来源source字段说明 | |||
| 类型|说明 | |||
| --------- | ----------- | |||
| |IssueAssigned | 有新指派给我的易修 | | |||
| |IssueAssignerExpire | 我负责的易修截止日期到达最后一天 | | |||
| |IssueAtme | 在易修中@我 | | |||
| |IssueChanged | 我创建或负责的易修状态变更 | | |||
| |IssueCreatorExpire | 我创建的易修截止日期到达最后一天 | | |||
| |IssueDeleted | 我创建或负责的易修删除 | | |||
| |IssueJournal | 我创建或负责的易修有新的评论 | | |||
| |LoginIpTip | 登录异常提示 | | |||
| |OrganizationJoined | 账号被拉入组织 | | |||
| |OrganizationLeft | 账号被移出组织 | | |||
| |OrganizationRole | 账号组织权限变更 | | |||
| |ProjectDeleted | 我关注的仓库被删除 | | |||
| |ProjectFollowed | 我管理的仓库被关注 | | |||
| |ProjectForked | 我管理的仓库被复刻 | | |||
| |ProjectIssue | 我管理/关注的仓库有新的易修 | | |||
| |ProjectJoined | 账号被拉入项目 | | |||
| |ProjectLeft | 账号被移出项目 | | |||
| |ProjectMemberJoined | 我管理的仓库有成员加入 | | |||
| |ProjectMemberLeft | 我管理的仓库有成员移出 | | |||
| |ProjectMilestone | 我管理的仓库有新的里程碑 | | |||
| |ProjectPraised | 我管理的仓库被点赞 | | |||
| |ProjectPullRequest | 我管理/关注的仓库有新的合并请求 | | |||
| |ProjectRole | 账号仓库权限变更 | | |||
| |ProjectSettingChanged | 我管理的仓库项目设置被更改 | | |||
| |ProjectTransfer | 我关注的仓库被转移 | | |||
| |ProjectVersion | 我关注的仓库有新的发行版 | | |||
| |PullRequestAssigned | 有新指派给我的合并请求 | | |||
| |PullReuqestAtme | 在合并请求中@我 | | |||
| |PullRequestChanged | 我创建或负责的合并请求状态变更 | | |||
| |PullRequestClosed | 我创建或负责的合并请求被关闭 | | |||
| |PullRequestJournal | 我创建或负责的合并请求有新的评论 | | |||
| |PullRequestMerged | 我创建或负责的合并请求被合并 | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "total_count": 5, | |||
| "type": "", | |||
| "unread_notification": 3, | |||
| "unread_atme": 2, | |||
| "messages": [ | |||
| { | |||
| "id": 1, | |||
| "status": 1, | |||
| "content": "Atme Message Content 1", | |||
| "notification_url": "http://www.baidu.com", | |||
| "source": "PullRequestAtme", | |||
| "time_ago": "1天前", | |||
| "type": "atme", | |||
| "sender": { | |||
| "id": 5, | |||
| "type": "User", | |||
| "name": "testforge2", | |||
| "login": "testforge2", | |||
| "image_url": "system/lets/letter_avatars/2/T/236_177_85/120.png" | |||
| } | |||
| }, | |||
| { | |||
| "id": 2, | |||
| "status": 0, | |||
| "content": "Atme Message Content 2", | |||
| "notification_url": "http://www.baidu.com", | |||
| "source": "IssueAtme", | |||
| "time_ago": "1天前", | |||
| "type": "atme", | |||
| "sender": { | |||
| "id": 4, | |||
| "type": "User", | |||
| "name": "testforge1", | |||
| "login": "testforge1", | |||
| "image_url": "system/lets/letter_avatars/2/T/19_237_174/120.png" | |||
| } | |||
| }, | |||
| { | |||
| "id": 3, | |||
| "status": 1, | |||
| "content": "Notification Message Content 1", | |||
| "notification_url": "http://www.baidu.com", | |||
| "source": "IssueDelete", | |||
| "time_ago": "1天前", | |||
| "type": "notification" | |||
| }, | |||
| { | |||
| "id": 4, | |||
| "status": 0, | |||
| "content": "Notification Message Content 2", | |||
| "notification_url": "http://www.baidu.com", | |||
| "source": "IssueChanged", | |||
| "time_ago": "1天前", | |||
| "type": "notification" | |||
| }, | |||
| { | |||
| "id": 5, | |||
| "status": 0, | |||
| "content": "Notification Message Content 3", | |||
| "notification_url": "http://www.baidu.com", | |||
| "source": "ProjectJoined", | |||
| "time_ago": "1天前", | |||
| "type": "notification" | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 用户阅读系统通知 | |||
| 用户阅读系统通知 | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST http://localhost:3000/api/users/yystopf/system_notification_histories.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/users/:login/system_notification_histories.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST /api/users/:login/system_notification_histories.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |system_notification_id |integer |阅读的系统通知id | | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| ## 发送消息 | |||
| 发送消息, 目前只支持atme | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST http://localhost:3000/api/users/:login/messages.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/users/:login/messages.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST api/users/yystopf/messages.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type | string | 消息类型 | | |||
| |receivers_login | array | 需要发送消息的用户名数组| | |||
| |atmeable_type | string | atme消息对象,是从哪里@我的,比如评论:Journal、易修:Issue、合并请求:PullRequest | | |||
| |atmeable_id | integer | atme消息对象id | | |||
| > 请求的JSON示例: | |||
| ```json | |||
| { | |||
| "type": "atme", | |||
| "receivers_login": ["yystopf", "testforge1"], | |||
| "atmeable_type": "Journal", | |||
| "atmeable_id": 67 | |||
| } | |||
| ``` | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 阅读消息 | |||
| 阅读消息 | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST http://localhost:3000/api/users/:login/messages/read.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/users/:login/messages/read.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST api/users/yystopf/messages/read.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type | string | 消息类型,不传为所有消息,notification为系统消息,atme为@我消息| | |||
| |ids | array | 消息id数组,包含-1则把所有未读消息标记为已读| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 删除消息 | |||
| 删除消息 | |||
| > 示例: | |||
| ```shell | |||
| curl -X DELETE http://localhost:3000/api/users/:login/messages.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('DELETE /api/users/:login/messages.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `DELETE api/users/yystopf/messages.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type | string | 消息类型,atme为@我消息| | |||
| |ids | array | 消息id数组,包含-1则把所有消息删除| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 更改用户信息 | |||
| 更改用户信息 | |||
| @@ -103,6 +402,290 @@ await octokit.request('PATCH/PUT /api/users/:login.json') | |||
| "message": "success" | |||
| } | |||
| ``` | |||
| ## 获取平台消息设置配置信息 | |||
| 获取平台消息设置配置信息 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/template_message_settings.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/template_message_settings.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/template_message_settings.json` | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |type |string |消息配置类型 | | |||
| |type_name |string |消息配置类型含义| | |||
| |total_settings_count |int |配置条数| | |||
| |settings.name |string |配置名称| | |||
| |settings.key |string |配置标识| | |||
| |settings.notification_disabled |boolean |站内信设置是否禁用| | |||
| |settings.email_disabled |boolean |邮件设置是否禁用| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "响应成功", | |||
| "setting_types": [ | |||
| { | |||
| "type": "TemplateMessageSetting::Normal", | |||
| "type_name": "", | |||
| "total_settings_count": 3, | |||
| "settings": [ | |||
| { | |||
| "name": "被拉入或移出组织", | |||
| "key": "Organization", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "被拉入或移出项目", | |||
| "key": "Project", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "有权限变更", | |||
| "key": "Permission", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "type": "TemplateMessageSetting::CreateOrAssign", | |||
| "type_name": "我创建的或负责的", | |||
| "total_settings_count": 4, | |||
| "settings": [ | |||
| { | |||
| "name": "易修被指派", | |||
| "key": "IssueAssigned", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "合并请求被指派", | |||
| "key": "PullRequestAssigned", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "type": "TemplateMessageSetting::ManageProject", | |||
| "type_name": "我管理的仓库", | |||
| "total_settings_count": 4, | |||
| "settings": [ | |||
| { | |||
| "name": "有新的易修", | |||
| "key": "Issue", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "有新的合并请求", | |||
| "key": "PullRequest", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "有成员变动", | |||
| "key": "Member", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| }, | |||
| { | |||
| "name": "设置更改", | |||
| "key": "SettingChanged", | |||
| "notification_disabled": true, | |||
| "email_disabled": false | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取用户消息设置配置信息 | |||
| 获取用户消息设置配置信息 | |||
| > 示例: | |||
| ```shell | |||
| curl -X GET http://localhost:3000/api/users/yystopf/template_message_settings.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('GET /api/uses/yystopf/template_message_settings.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `GET /api/users/:user_id/template_message_settings.json` | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |notification_body |string |站内信配置 | | |||
| |email_body |string |邮件配置| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "响应成功", | |||
| "user": { | |||
| "id": 2, | |||
| "type": "User", | |||
| "name": "heh", | |||
| "login": "yystopf", | |||
| "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" | |||
| }, | |||
| "notification_body": { | |||
| "CreateOrAssign::IssueAssigned": true, | |||
| "CreateOrAssign::PullRequestAssigned": true, | |||
| "ManageProject::Issue": true, | |||
| "ManageProject::PullRequest": true, | |||
| "ManageProject::Member": true, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": true, | |||
| "Normal::Project": true, | |||
| "Normal::Permission": true | |||
| }, | |||
| "email_body": { | |||
| "CreateOrAssign::IssueAssigned": false, | |||
| "CreateOrAssign::PullRequestAssigned": false, | |||
| "ManageProject::Issue": false, | |||
| "ManageProject::PullRequest": false, | |||
| "ManageProject::Member": false, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": false, | |||
| "Normal::Project": true, | |||
| "Normal::Permission": false | |||
| } | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 重新设置用户消息设置配置信息 | |||
| 重新设置用户消息设置配置信息 | |||
| > 示例: | |||
| ```shell | |||
| curl -X POST http://localhost:3000/api/users/yystopf/template_message_settings/update_setting.json | |||
| ``` | |||
| ```javascript | |||
| await octokit.request('POST /api/uses/yystopf/template_message_settings/update_setting.json') | |||
| ``` | |||
| ### HTTP 请求 | |||
| `POST /api/users/:user_id/template_message_settings/update_setting.json` | |||
| ### 请求字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |notification_body |string |站内信配置 | | |||
| |email_body |string |邮件配置| | |||
| > 请求的JSON示例: | |||
| ```json | |||
| { | |||
| "setting": { | |||
| "notification_body": { | |||
| "CreateOrAssign::IssueAssigned": true, | |||
| "CreateOrAssign::PullRequestAssigned": true, | |||
| "ManageProject::Issue": true, | |||
| "ManageProject::PullRequest": true, | |||
| "ManageProject::Member": true, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": true, | |||
| "Normal::Project": true, | |||
| "Normal::Permission": true | |||
| }, | |||
| "email_body": { | |||
| "CreateOrAssign::IssueAssigned": false, | |||
| "CreateOrAssign::PullRequestAssigned": false, | |||
| "ManageProject::Issue": false, | |||
| "ManageProject::PullRequest": false, | |||
| "ManageProject::Member": false, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": false, | |||
| "Normal::Project": "t", | |||
| "Normal::Permission": false | |||
| } | |||
| } | |||
| } | |||
| ``` | |||
| ### 返回字段说明: | |||
| 参数 | 类型 | 字段说明 | |||
| --------- | ----------- | ----------- | |||
| |notification_body |string |站内信配置 | | |||
| |email_body |string |邮件配置| | |||
| > 返回的JSON示例: | |||
| ```json | |||
| { | |||
| "status": 0, | |||
| "message": "响应成功", | |||
| "user": { | |||
| "id": 2, | |||
| "type": "User", | |||
| "name": "heh", | |||
| "login": "yystopf", | |||
| "image_url": "system/lets/letter_avatars/2/H/188_239_142/120.png" | |||
| }, | |||
| "notification_body": { | |||
| "CreateOrAssign::IssueAssigned": true, | |||
| "CreateOrAssign::PullRequestAssigned": true, | |||
| "ManageProject::Issue": true, | |||
| "ManageProject::PullRequest": true, | |||
| "ManageProject::Member": true, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": true, | |||
| "Normal::Project": true, | |||
| "Normal::Permission": true | |||
| }, | |||
| "email_body": { | |||
| "CreateOrAssign::IssueAssigned": false, | |||
| "CreateOrAssign::PullRequestAssigned": false, | |||
| "ManageProject::Issue": false, | |||
| "ManageProject::PullRequest": false, | |||
| "ManageProject::Member": false, | |||
| "ManageProject::SettingChanged": true, | |||
| "Normal::Organization": false, | |||
| "Normal::Project": true, | |||
| "Normal::Permission": false | |||
| } | |||
| } | |||
| ``` | |||
| <aside class="success"> | |||
| Success Data. | |||
| </aside> | |||
| ## 获取用户星标项目 | |||
| 获取用户星标项目 | |||
| @@ -1,10 +0,0 @@ | |||
| class AddSchoolApplyForm | |||
| include ActiveModel::Model | |||
| attr_accessor :name, :province, :city, :address, :remarks | |||
| validates :name, presence: true | |||
| # validates :province, presence: true | |||
| # validates :city, presence: true | |||
| # validates :address, presence: true | |||
| end | |||
| @@ -1,27 +0,0 @@ | |||
| class ApplyShixunMirrorForm | |||
| include ActiveModel::Model | |||
| attr_accessor :language, :runtime, :run_method, :attachment_id | |||
| validates :language, presence: true | |||
| validates :runtime, presence: true | |||
| validates :run_method, presence: true | |||
| validates :attachment_id, presence: true, numericality: { only_integer: true } | |||
| validate :ensure_attachment_presence | |||
| def ensure_attachment_presence | |||
| return unless attachment_id | |||
| if attachment.blank? | |||
| errors.add(:attachment_id, :attachment_not_exist) | |||
| end | |||
| end | |||
| def attachment | |||
| @attachment ||= Attachment.find_by_id(attachment_id) | |||
| end | |||
| def to_json | |||
| { language: language, runtime: runtime, run_method: run_method, attachment_id: attachment_id }.to_json | |||
| end | |||
| end | |||
| @@ -2,18 +2,32 @@ class BaseForm | |||
| include ActiveModel::Model | |||
| def check_project_category(project_category_id) | |||
| raise "project_category_id参数值无效." if (ProjectCategory.find_by_id project_category_id).blank? | |||
| unless project_category_id == '' | |||
| raise "project_category_id参数值无效." if project_category_id && !ProjectCategory.exists?(project_category_id) | |||
| end | |||
| end | |||
| def check_project_language(project_language_id) | |||
| raise "project_language_id参数值无效." if (ProjectLanguage.find_by_id project_language_id).blank? | |||
| unless project_language_id == '' | |||
| raise "project_language_id参数值无效." if project_language_id && !ProjectLanguage.exists?(project_language_id) | |||
| end | |||
| end | |||
| def check_repository_name(user_id, repository_name) | |||
| raise "仓库名称已被使用." if Repository.where(user_id: user_id, identifier: repository_name.strip).exists? | |||
| check_reversed_keyword(repository_name) | |||
| raise "项目标识已被使用." if Repository.where(user_id: user_id, identifier: repository_name.strip).exists? | |||
| end | |||
| def check_project_name(user_id, project_name) | |||
| raise "项目名称已被使用." if Project.where(user_id: user_id, name: project_name.strip).exists? | |||
| end | |||
| def check_reversed_keyword(repository_name) | |||
| raise "项目标识已被占用." if ReversedKeyword.check_exists?(repository_name) | |||
| end | |||
| private | |||
| def strip(str) | |||
| str.to_s.strip.presence | |||
| end | |||
| end | |||
| @@ -1,15 +0,0 @@ | |||
| class ExaminationBanks::SaveExamForm | |||
| include ActiveModel::Model | |||
| attr_accessor :discipline_id, :sub_discipline_id, :difficulty, :name, :duration, :tag_discipline_id | |||
| validates :discipline_id, presence: true | |||
| validates :sub_discipline_id, presence: true | |||
| validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true } | |||
| validates :name, presence: true, length: { maximum: 60, too_long: "不能超过60个字符" } | |||
| validate :validate_duration | |||
| def validate_duration | |||
| raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1 | |||
| end | |||
| end | |||
| @@ -1,12 +0,0 @@ | |||
| class ExaminationIntelligentSettings::SaveExamForm | |||
| include ActiveModel::Model | |||
| attr_accessor :name, :duration | |||
| validates :name, presence: true, length: { maximum: 60 } | |||
| validate :validate_duration | |||
| def validate_duration | |||
| raise '时长应为大于0的整数' if duration.present? && duration.to_i < 1 | |||
| end | |||
| end | |||
| @@ -1,11 +0,0 @@ | |||
| class ExaminationIntelligentSettings::SaveExamSettingForm | |||
| include ActiveModel::Model | |||
| attr_accessor :discipline_id, :sub_discipline_id, :source, :difficulty, :tag_discipline_id, :question_settings | |||
| validates :discipline_id, presence: true | |||
| validates :sub_discipline_id, presence: true | |||
| validates :source, presence: true | |||
| validates :difficulty, presence: true, inclusion: {in: 1..3}, numericality: { only_integer: true } | |||
| validates :question_settings, presence: true | |||
| end | |||
| @@ -5,7 +5,7 @@ class Issues::CreateForm | |||
| validates :subject, presence: { message: "不能为空" } | |||
| validates :subject, length: { maximum: 80, too_long: "不能超过80个字符" } | |||
| validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" } | |||
| end | |||
| @@ -5,6 +5,6 @@ class Issues::UpdateForm | |||
| validates :subject, presence: { message: "不能为空" } | |||
| validates :subject, length: { maximum: 80, too_long: "不能超过80个字符" } | |||
| validates :subject, length: { maximum: 200, too_long: "不能超过200个字符" } | |||
| end | |||
| @@ -0,0 +1,23 @@ | |||
| class Notice::Write::CreateAtmeForm | |||
| include ActiveModel::Model | |||
| attr_accessor :receivers_login, :atmeable_type, :atmeable_id | |||
| validate :check_receivers | |||
| def check_receivers | |||
| receivers_login.each do |login| | |||
| receiver = User.find_by(login: login) || User.find_by_id(login) | |||
| raise 'receivers_login值无效.' unless receiver.present? | |||
| end | |||
| end | |||
| def check_atmeable | |||
| begin | |||
| raise 'atmeable对象无效.' unless atmeable_type.constantize.find_by_id(atmeable_id).present? | |||
| rescue => exception | |||
| raise 'atmeable对象无效.' | |||
| end | |||
| end | |||
| end | |||
| @@ -1,11 +1,9 @@ | |||
| class Projects::CreateForm < BaseForm | |||
| REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| attr_accessor :user_id, :name, :description, :repository_name, :project_category_id, | |||
| :project_language_id, :ignore_id, :license_id, :private, :owner | |||
| validates :user_id, :name, :description,:repository_name, | |||
| :project_category_id, :project_language_id, presence: true | |||
| validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :user_id, :name, :repository_name, presence: true | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :name, length: { maximum: 50 } | |||
| validates :repository_name, length: { maximum: 100 } | |||
| @@ -15,6 +13,8 @@ class Projects::CreateForm < BaseForm | |||
| validate do | |||
| check_project_category(project_category_id) | |||
| check_project_language(project_language_id) | |||
| check_project_name(user_id, name) unless name.blank? | |||
| check_repository_name(user_id, repository_name) unless repository_name.blank? | |||
| end | |||
| def check_license | |||
| @@ -26,13 +26,14 @@ class Projects::CreateForm < BaseForm | |||
| end | |||
| def check_owner | |||
| @owner = Owner.find_by(id: user_id) | |||
| raise "user_id值无效." if user_id && owner.blank? | |||
| @project_owner = Owner.find_by(id: user_id) | |||
| raise "user_id值无效." if user_id && @project_owner.blank? | |||
| end | |||
| def check_max_repo_creation | |||
| return unless owner.is_a?(Organization) | |||
| return if owner.max_repo_creation <= -1 | |||
| raise "已超过组织设置最大仓库数" if owner.max_repo_creation == owner.num_projects | |||
| return unless @project_owner.is_a?(Organization) | |||
| return if @project_owner.max_repo_creation <= -1 | |||
| raise "已超过组织设置最大仓库数" if @project_owner.max_repo_creation == @project_owner.num_projects | |||
| end | |||
| end | |||
| @@ -1,12 +1,13 @@ | |||
| class Projects::MigrateForm < BaseForm | |||
| REPOSITORY_NAME_REGEX = /^(?!_)(?!.*?_$)[a-zA-Z0-9_-]+$/ #只含有数字、字母、下划线不能以下划线开头和结尾 | |||
| URL_REGEX = /\A(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?\z/i | |||
| attr_accessor :user_id, :name, :repository_name, :project_category_id, :description, | |||
| :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner | |||
| attr_accessor :user_id, :name, :description, :repository_name, :project_category_id, :project_language_id, :clone_addr, :private, :is_mirror, :auth_username, :auth_password, :owner | |||
| validates :user_id, :name, :description,:repository_name, :project_category_id, :project_language_id, presence: true | |||
| validates :repository_name, format: { with: REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :clone_addr, format: { with: URL_REGEX, multiline: true, message: "地址格式不正确" } | |||
| validates :user_id, :name, :repository_name, :clone_addr, presence: true | |||
| validates :repository_name, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validates :clone_addr, format: { with: CustomRegexp::URL_REGEX, multiline: true, message: "地址格式不正确" } | |||
| validates :name, length: { maximum: 50 } | |||
| validates :repository_name, length: { maximum: 100 } | |||
| validates :description, length: { maximum: 200 } | |||
| validate do | |||
| check_project_name(user_id, name) unless name.blank? | |||
| check_repository_name(user_id, repository_name) unless repository_name.blank? | |||
| @@ -1,11 +1,15 @@ | |||
| class Projects::UpdateForm < BaseForm | |||
| attr_accessor :name, :description, :project_category_id, :project_language_id, :private | |||
| validates :name, :description, :project_category_id, :project_language_id, presence: true | |||
| attr_accessor :name, :description, :project_category_id, :project_language_id, :private, :identifier, :user_id, :project_identifier | |||
| validates :name, presence: true | |||
| validates :name, length: { maximum: 50 } | |||
| validates :description, length: { maximum: 200 } | |||
| validates :identifier, format: { with: CustomRegexp::REPOSITORY_NAME_REGEX, multiline: true, message: "只能含有数字、字母、下划线且不能以下划线开头和结尾" } | |||
| validate do | |||
| check_project_category(project_category_id) | |||
| check_project_language(project_language_id) | |||
| check_repository_name(user_id, identifier) unless identifier.blank? || identifier == project_identifier | |||
| end | |||
| end | |||
| @@ -0,0 +1,8 @@ | |||
| class Projects::Webhooks::CreateForm < BaseForm | |||
| attr_accessor :type, :url, :http_method, :content_type, :secret, :events, :active, :branch_filter | |||
| validates :url, format: { with: URI::regexp(%w[http https]), message: "请输入正确的地址" } | |||
| validates :active, inclusion: {in: [true, false]} | |||
| validates :http_method, inclusion: { in: %w(POST GET), message: "请输入正确的请求方式"} | |||
| validates :content_type, inclusion: { in: %w(json form), message: "请输入正确的Content Type"} | |||
| end | |||
| @@ -0,0 +1,51 @@ | |||
| module Register | |||
| class BaseForm < ::BaseForm | |||
| include ActiveModel::Model | |||
| Error = Class.new(StandardError) | |||
| EmailError = Class.new(Error) | |||
| LoginError = Class.new(Error) | |||
| PhoneError = Class.new(Error) | |||
| PasswordFormatError = Class.new(Error) | |||
| VerifiCodeError = Class.new(Error) | |||
| private | |||
| def check_login(login) | |||
| login = strip(login) | |||
| raise LoginError, "登录名格式有误" unless login =~ CustomRegexp::LOGIN | |||
| login_exist = Owner.exists?(login: login) || ReversedKeyword.check_exists?(login) | |||
| raise LoginError, '登录名已被使用' if login_exist | |||
| end | |||
| def check_mail(mail) | |||
| mail = strip(mail) | |||
| raise EmailError, "邮件格式有误" unless mail =~ CustomRegexp::EMAIL | |||
| mail_exist = Owner.exists?(mail: mail) | |||
| raise EmailError, '邮箱已被使用' if mail_exist | |||
| end | |||
| def check_phone(phone) | |||
| phone = strip(phone) | |||
| raise PhoneError, "手机号格式有误" unless phone =~ CustomRegexp::PHONE | |||
| phone_exist = Owner.exists?(phone: phone) | |||
| raise PhoneError, '手机号已被使用' if phone_exist | |||
| end | |||
| def check_password(password) | |||
| password = strip(password) | |||
| raise PasswordFormatError, "8~16位密码,支持字母数字和符号" unless password =~ CustomRegexp::PASSWORD | |||
| end | |||
| def check_verifi_code(verifi_code, code) | |||
| code = strip(code) | |||
| # return if code == "123123" # TODO 万能验证码,用于测试 | |||
| raise VerifiCodeError, "验证码不正确" if verifi_code&.code != code | |||
| raise VerifiCodeError, "验证码已失效" if !verifi_code&.effective? | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,19 @@ | |||
| module Register | |||
| class CheckColumnsForm < Register::BaseForm | |||
| attr_accessor :type, :value | |||
| validates :type, presence: true, numericality: true | |||
| validates :value, presence: true | |||
| validate :check! | |||
| def check! | |||
| # params[:type] 为事件类型 1:登录名(login) 2:email(邮箱) 3:phone(手机号) | |||
| case strip(type).to_i | |||
| when 1 then check_login(strip(value)) | |||
| when 2 then check_mail(strip(value)) | |||
| when 3 then check_phone(strip(value)) | |||
| else raise("type值无效") | |||
| end | |||
| end | |||
| end | |||
| end | |||
| @@ -0,0 +1,27 @@ | |||
| module Register | |||
| class Form < Register::BaseForm | |||
| # login 登陆方式,支持邮箱、登陆、手机号等 | |||
| # namespace 用户空间地址 | |||
| # type: 1:手机号注册;2:邮箱注册 | |||
| attr_accessor :login, :namespace, :password, :code, :type | |||
| validates :login, :code, :password, :namespace, presence: true | |||
| validate :check! | |||
| def check! | |||
| Rails.logger.info "Register::Form params: code: #{code}; login: #{login}; namespace: #{namespace}; password: #{password}; type: #{type}" | |||
| db_verifi_code = | |||
| if type == 1 | |||
| check_phone(login) | |||
| VerificationCode.where(phone: login, code: code, code_type: 1).last | |||
| elsif type == 0 | |||
| check_mail(login) | |||
| VerificationCode.where(email: login, code: code, code_type: 8).last | |||
| end | |||
| check_login(namespace) | |||
| check_verifi_code(db_verifi_code, code) | |||
| check_password(password) | |||
| end | |||
| end | |||
| end | |||
| @@ -1,20 +0,0 @@ | |||
| class Weapps::CreateCourseForm | |||
| include ActiveModel::Model | |||
| attr_accessor :course | |||
| attr_accessor :name, :course_list_name, :credit, :course_module_types, :end_date | |||
| validates :name, presence: true | |||
| validates :course_list_name, presence: true | |||
| validate :course_name_prefix | |||
| validate :check_course_modules | |||
| def course_name_prefix | |||
| raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0 | |||
| end | |||
| def check_course_modules | |||
| raise '请至少添加一个课堂模块' if course_module_types.blank? | |||
| end | |||
| end | |||
| @@ -1,15 +0,0 @@ | |||
| class Weapps::UpdateCourseForm | |||
| include ActiveModel::Model | |||
| attr_accessor :course | |||
| attr_accessor :name, :course_list_name, :credit, :end_date | |||
| validates :name, presence: true | |||
| validates :course_list_name, presence: true | |||
| validate :course_name_prefix | |||
| def course_name_prefix | |||
| raise '课堂名称应以课程名称开头' unless name.index(course_list_name) && name.index(course_list_name) == 0 | |||
| end | |||
| end | |||