Skip to content

Commit

Permalink
GH-17 Implement lightweight dashboard (Resolve #17)
Browse files Browse the repository at this point in the history
  • Loading branch information
dzikoysk committed Jun 15, 2020
1 parent aec0551 commit ab96fe5
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .run/Reposilite.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
<option name="MAIN_CLASS_NAME" value="org.panda_lang.reposilite.Reposilite" />
<module name="reposilite" />
<option name="VM_PARAMETERS" value="-Xmx8M -Dreposilite.debugEnabled=true" />
<option name="VM_PARAMETERS" value="-Xmx12M -Dreposilite.debugEnabled=true" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/reposilite-backend/src/test/workspace" />
<extension name="coverage">
<pattern>
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It is a simple solution to replace managers like Nexus, Archiva or Artifactory.
* [x] Statistics
* [x] REST API
* [x] Repository browser
* [ ] Admin panel
* [x] Dashboard (+ Admin panel)
* [ ] Docs

#### Installation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public final class ReposiliteHttpServer {
void start(Configuration configuration, Runnable onStart) {
LookupService lookupService = new LookupService(reposilite);
LookupController lookupController = new LookupController(reposilite.getFrontend(), lookupService);
DeployController deployController = new DeployController(reposilite);

this.javalin = Javalin.create(config -> config(configuration, config))
.get("/api/auth", new AuthApiController(configuration, reposilite.getAuthenticator()))
Expand All @@ -50,7 +51,8 @@ void start(Configuration configuration, Runnable onStart) {
.get("/js/app.js", new FrontendController(reposilite))
.get("/*", lookupController)
.head("/*", lookupController)
.put("/*", new DeployController(reposilite))
.put("/*", deployController)
.post("/*", deployController)
.before(ctx -> reposilite.getStatsService().record(ctx.req.getRequestURI()))
.exception(Exception.class, (exception, ctx) -> reposilite.throwException(ctx.req.getRequestURI(), exception))
.start(configuration.getHostname(), configuration.getPort());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,6 @@ public Result<Context, String> deploy(Context context) {
return Result.error(authResult.getError().get());
}

Session session = authResult.getValue().get();

ArtifactFile targetFile = ArtifactFile.fromURL(context.req.getRequestURI());
File file = targetFile.getFile();

Expand Down
10 changes: 8 additions & 2 deletions reposilite-backend/src/main/resources/frontend/js/app.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion reposilite-backend/src/test/workspace/reposilite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ rewritePathsEnabled: true
fullAuthEnabled: false
# If you don't want to display content of your repositories,
# you can just disable indexing
indexingEnabled: false
indexingEnabled: true
# List of management tokens used by dashboard to access extra options.
# (By default, people are allowed to use standard dashboard options related to the associated path)
managers: []
Expand Down
8 changes: 6 additions & 2 deletions reposilite-backend/src/test/workspace/stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
!!org.panda_lang.reposilite.stats.StatsEntity
"records":
"/api/auth": !!int "6"
"/api/": !!int "19"
"/api/releases/auth/test": !!int "58"
"/api/configuration": !!int "42"
"/api/auth": !!int "29"
"/////hello.panda": !!int "33"
"/api/": !!int "333"
"/////test.panda": !!int "8"
4 changes: 3 additions & 1 deletion reposilite-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
"tailwindcss": "^1.4.6",
"vue": "^2.6.11",
"vue-meta": "^2.3.4",
"vue-router": "^3.3.1"
"vue-notification": "^1.3.20",
"vue-router": "^3.3.1",
"vue-upload-component": "^2.8.20"
},
"devDependencies": {
"@babel/core": "^7.10.1",
Expand Down
18 changes: 0 additions & 18 deletions reposilite-frontend/src/components/Login.vue

This file was deleted.

13 changes: 8 additions & 5 deletions reposilite-frontend/src/main.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import './assets/tailwind.css'

import Vue from 'vue'
import App from './App.vue'
import Axios from 'axios'
import router from './router'
import './assets/tailwind.css'
import fontawesome from '@fortawesome/fontawesome'
import Notifications from 'vue-notification'
import Meta from 'vue-meta'
import mixins from './mixins'

Vue.config.productionTip = false
Vue.prototype.$http = Axios
import fontawesome from '@fortawesome/fontawesome'

fontawesome.config = { autoReplaceSvg: false }

Vue.use(Meta)
Vue.config.productionTip = false
Vue.prototype.$http = Axios

Vue.use(Notifications)
Vue.use(Meta)
Vue.mixin(mixins)

new Vue({
Expand Down
15 changes: 12 additions & 3 deletions reposilite-frontend/src/mixins.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
const url = ((process.env.NODE_ENV == 'production') ? '/' : 'http://localhost:80/') + 'api';
const url = (process.env.NODE_ENV == 'production') ? '/' : 'http://localhost:80/';
const apiUrl = url + 'api';

export default {
methods: {
api(uri, credentials) {
return this.$http.get(url + uri, { auth: credentials })
api(uri, auth) {
return this.$http.get(apiUrl + uri, {
auth: {
username: auth.alias,
password: auth.token
}
})
},
parentPath() {
const elements = ('/' + this.getQualifier()).split('/')
Expand All @@ -28,6 +34,9 @@ export default {
}

return uri
},
url() {
return url
}
}
}
56 changes: 19 additions & 37 deletions reposilite-frontend/src/views/Dashboard.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
<template lang="pug">
#app.flex.justify-center.items-center
form#login(v-if="!auth.verified").p-8.text-center(method="post").border-dashed.border-black.border-1.rounded
h1.font-bold.py-4.text-xl Login
form#login(v-if="!auth.verified" method="post").p-8.text-center.border-dashed.border-black.rounded.bg-white
h1.font-bold.pb-4.text-xl Login
div.py-1
input(placeholder="Alias" name="alias" v-model="auth.alias").w-96.p-1
input(placeholder="Alias" name="alias" v-model="auth.alias").w-96.bg-gray-100.p-2.rounded
div.py-1
input(placeholder="Token" name="token" v-model="auth.token" autocomple="on").w-96.p-1
input(placeholder="Token" name="token" v-model="auth.token" autocomple="on").w-96.bg-gray-100.p-2.rounded
div.py-1.text-right.px-2.mt-1
router-link(:to="this.qualifier").text-blue-400.text-xs ← Back to index
div.py-3
button.bg-gray-300.px-6.py-1.mt-1(v-on:click="login") Login
div.py-1
p(v-if="error") {{ this.error }}
button(v-on:click="login").bg-gray-200.px-6.py-1.mt-1.w-96 Login
notifications(group="login" position="center top")
#panel(v-else).p-6.container
header.pb-4
router-link(to="/dashboard").px-4 Index
Expand All @@ -28,60 +29,41 @@ const defaultAuth = {
token: '',
path: '',
manager: false,
verified: false
verified: false,
qualifier: ''
}
export default {
data: () => ({
error: undefined,
auth: Object.assign({}, defaultAuth),
qualifier: '',
files: [],
qualifier: ''
}),
components: {
FileEntry
},
mounted() {
this.qualifier = this.getQualifier()
if (sessionStorage.auth) {
this.auth = JSON.parse(sessionStorage.auth)
this.list()
}
},
watch: {
$route() {
this.list()
}
},
methods: {
login(event) {
event.preventDefault()
this.api('/auth', {
username: this.auth.alias,
password: this.auth.token
}).then(response => {
this.api('/auth', this.auth).then(response => {
this.auth.verified = true
this.auth.path = response.data.path
this.auth.manager = response.data.manager
sessionStorage.auth = JSON.stringify(this.auth)
this.list()
}).catch(err => {
this.error = err.response.status + ': ' + err.response.data.message
})
},
list() {
const qualifier = this.getQualifier()
console.log(this.qualifier)
this.api(qualifier, {
username: this.auth.alias,
password: this.auth.token
}).then(response => {
this.files = response.data.files
this.qualifier = qualifier
}).catch(err => {
console.log(err)
this.error = err.status + ': ' + err.response.data.message
this.$notify({
group: 'login',
type: 'error',
title: err.response.data.message
})
})
},
logout() {
Expand Down
11 changes: 7 additions & 4 deletions reposilite-frontend/src/views/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
.flex.justify-between.py-4
h1.text-xl
| Index of
span.ml-1 {{ this.qualifier }}
span.ml-2 {{ this.qualifier }}
router-link(:to="'/dashboard' + this.qualifier")
span.ml-3(:style="'color: ' + this.configuration.accentColor")
i.fas.fa-feather-alt
router-link(
v-if="this.qualifier != undefined && this.qualifier.length > 0"
:to='parentPath()'
Expand Down Expand Up @@ -63,7 +66,7 @@ export default {
created() {
this.message = window.REPOSILITE_MESSAGE
this.api('/configuration')
this.api('/configuration', {})
.then(response => (this.configuration = response.data))
.catch(err => (this.response = err.response.data))
},
Expand All @@ -79,7 +82,7 @@ export default {
updateEntities() {
this.qualifier = this.getQualifier()
this.api(this.qualifier)
this.api(this.qualifier, {})
.then(response => (this.response = response.data))
.catch(err => (this.response = err.response.data))
},
Expand All @@ -95,7 +98,7 @@ export default {

<style lang="stylus">
html
background-color #f3f3f3
background-color #f1f1f1
#app
font-family 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif
-webkit-font-smoothing antialiased
Expand Down
59 changes: 55 additions & 4 deletions reposilite-frontend/src/views/dashboard/Index.vue
Original file line number Diff line number Diff line change
@@ -1,23 +1,74 @@
<template lang="pug">
div(v-if="this.$parent.files")
div(v-if="this.files")
.flex.justify-between.pb-6.pt-2.px-2
h1.font-bold
| Index of
span.ml-1 {{ this.$parent.qualifier }}
router-link(v-if="this.$parent.qualifier != undefined && this.$parent.qualifier.length > 0" :to="'/dashboard' + parentPath()") ← Back
span.ml-1 {{ this.qualifier }}
router-link(v-if="this.qualifier != undefined && this.qualifier.length > 0" :to="'/dashboard' + parentPath()") ← Back
FileEntry(
v-for="file in this.$parent.files"
v-for="file in files"
:key="file.name"
:file="file"
)
h1(v-if="files && files.length === 0").px-2 Directory is empty
notifications(group="index" position="center top")
</template>

<script>
import FileEntry from '../../components/FileEntry'
export default {
data() {
return {
qualifier: '',
files: [],
auth: this.$parent.auth
}
},
components: {
FileEntry
},
mounted() {
this.update()
},
watch: {
$route() {
this.update()
}
},
methods: {
update() {
if (!this.redirect()) {
this.list()
}
},
redirect() {
const path = this.auth.path.replace('\\', '/')
if (!this.$route.fullPath.includes(path)) {
this.$router.push({ path: `/dashboard${path}` })
return true
}
return false
},
list() {
const qualifier = this.getQualifier()
this.api(qualifier, this.$parent.auth).then(response => {
this.files = response.data.files
this.qualifier = qualifier
}).catch(err => {
this.$notify({
group: 'index',
type: 'warn',
title: 'Indexing is not available',
text: err.response.status + ': ' + err.response.data.message
})
})
this.qualifier = qualifier
}
}
}
</script>
2 changes: 1 addition & 1 deletion reposilite-frontend/src/views/dashboard/Settings.vue
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<template lang="pug">
div
div #soon™
</template>
Loading

0 comments on commit ab96fe5

Please sign in to comment.