Skip to content

Commit

Permalink
✨ User management (#2499)
Browse files Browse the repository at this point in the history
* ✨ Create user from admin panel
* 🚧 Store registration preference
* ⬆️ Upgrade packages
* 🚧 Disable search on client side
* 🗑️ Remove usage of global
* ✨ Implement option to require login everywhere
  • Loading branch information
moisout authored Jan 3, 2024
1 parent 577f161 commit 7131c8c
Show file tree
Hide file tree
Showing 49 changed files with 1,695 additions and 728 deletions.
15 changes: 12 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
This file is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.14.3]
## [0.15.0]

### Added

- Add option to disable registration to admin panel [#2499](https://github.com/ViewTube/viewtube/pull/2499)
- Add option to require login everywhere to admin panel [#2499](https://github.com/ViewTube/viewtube/pull/2499)
- Allow users to be added via the admin panel [#2499](https://github.com/ViewTube/viewtube/pull/2499)

### Fixed

- Use loadeddata event to fix video player aspect ratio (Thanks @themisir) [#2505](https://github.com/ViewTube/viewtube/pull/2505)
- Use value of apiUrl, fixes account deletion issue (Thanks @alvanrahimli) [#2515](https://github.com/ViewTube/viewtube/pull/2515)
- Fix logout not working [#2499](https://github.com/ViewTube/viewtube/pull/2499)

## [0.14.2]

Expand Down Expand Up @@ -64,11 +71,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Reworked authentication with new devices interface in profile [#2186](https://github.com/ViewTube/viewtube/pull/2186)
- Add support for socks proxies [#2269](https://github.com/ViewTube/viewtube/pull/2269)

### Fixed

- Make client cookie logic more robust [#2259](https://github.com/ViewTube/viewtube/pull/2259)
- Upgrade packages and cleanup unused [#2261](https://github.com/ViewTube/viewtube/pull/2261)
- Fix history and profile page [#2282](https://github.com/ViewTube/viewtube/pull/2282)

## [0.12.2]

Expand Down Expand Up @@ -374,8 +383,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Initial release

[unreleased]: https://github.com/viewtube/viewtube/compare/v0.14.3...development
[0.14.3]: https://github.com/viewtube/viewtube/compare/v0.14.2...v0.14.3
[unreleased]: https://github.com/viewtube/viewtube/compare/v0.15.0...development
[0.14.3]: https://github.com/viewtube/viewtube/compare/v0.14.2...v0.15.0
[0.14.2]: https://github.com/viewtube/viewtube/compare/v0.14.1...v0.14.2
[0.14.1]: https://github.com/viewtube/viewtube/compare/v0.14.0...v0.14.1
[0.14.0]: https://github.com/viewtube/viewtube/compare/v0.13.1...v0.14.0
Expand Down
88 changes: 88 additions & 0 deletions client/components/admin/CreateUser.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<script setup lang="ts">
import { useMessagesStore } from '@/store/messages';
const username = ref('');
const password = ref('');
const { createUser } = useCreateUser();
const messagesStore = useMessagesStore();
const createUserAdmin = async () => {
if (username.value && password.value) {
const createdUser = await createUser({
username: username.value,
password: password.value
})
.then(
res => res,
reason => {
throw reason;
}
)
.catch(err => {
messagesStore.createMessage({
type: 'error',
title: 'Error creating user',
message: err?.data?.message ?? 'Unknown error'
});
});
username.value = '';
password.value = '';
if (createdUser) {
messagesStore.createMessage({
type: 'info',
title: 'User created',
message: `User ${createdUser.username} created`
});
}
}
};
</script>

<template>
<SectionSubtitle title="Create new user" />

<form id="create-user" class="create-user-form" method="post" @submit.prevent="createUserAdmin">
<FormInput id="username" v-model="username" type="username" label="Username" />
<FormInput id="password" v-model="password" type="password" label="Password" />
<FormSubmitButton :label="'Create user'" />
</form>
</template>

<style lang="scss" scoped>
.create-user-form {
display: flex;
flex-direction: row;
@media screen and (max-width: $mobile-width) {
flex-direction: column;
}
:deep(.submit-button) {
width: auto;
padding: 5px 15px;
margin: 16px 0;
}
:deep(.form-input > .input) {
margin-left: 0;
margin-right: 10px;
width: calc(100% - 10px);
@media screen and (max-width: $mobile-width) {
margin-right: 0;
width: 100%;
}
}
:deep(.form-input > .input-label) {
left: 14px;
}
:deep(.form-input > .form-input-icon) {
right: 25px;
}
}
</style>
74 changes: 74 additions & 0 deletions client/components/admin/UserManagement.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<script setup lang="ts">
import { useMessagesStore } from '~/store/messages';
const { apiUrl } = useApiUrl();
const { vtFetch } = useVtFetch();
const messagesStore = useMessagesStore();
const { data, refresh } = useGetServerSettings();
const onRegistrationEnabledChange = async (value: boolean) => {
await vtFetch(`${apiUrl.value}admin/server-settings`, {
method: 'POST',
body: {
registrationEnabled: value
}
});
await refresh();
await nextTick();
messagesStore.createMessage({
type: 'info',
title: 'Server settings updated',
message: `Public registration ${
value ? 'enabled' : 'disabled'
}. Restart the server for the changes to take effect.`
});
};
const onRequireLoginEverywhereChange = async (value: boolean) => {
await vtFetch(`${apiUrl.value}admin/server-settings`, {
method: 'POST',
body: {
requireLoginEverywhere: value
}
});
await refresh();
await nextTick();
messagesStore.createMessage({
type: 'info',
title: 'Server settings updated',
message: `Require login everywhere ${
value ? 'enabled' : 'disabled'
}. Restart the server for the changes to take effect.`
});
};
</script>

<template>
<div v-if="data" class="user-management">
<ButtonsSwitchButton
label="Enable public registration (restart required)"
:value="data.registrationEnabled"
small-label="Anyone can create an account"
small-label-negative="Accounts can only be created by the admin"
@valuechange="onRegistrationEnabledChange"
/>
<ButtonsSwitchButton
label="Require login everywhere (restart required)"
:value="data.requireLoginEverywhere"
small-label="Users must be logged in to access the site"
small-label-negative="Users can access the site without being logged in"
@valuechange="onRequireLoginEverywhereChange"
/>
</div>
<Spinner v-if="!data" />
</template>

<style lang="scss" scoped>
.user-management {
display: flex;
flex-direction: column;
align-items: flex-start;
margin: 0 0 15px 0;
}
</style>
14 changes: 14 additions & 0 deletions client/components/admin/Users.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<script setup lang="ts"></script>

<template>
<div class="admin-users">
<SectionSubtitle title="Options" />
<AdminUserManagement />
<AdminCreateUser />
</div>
</template>

<style lang="scss" scoped>
.admin-users {
}
</style>
26 changes: 21 additions & 5 deletions client/components/buttons/SwitchButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,17 @@
</div>
<div v-if="label" class="label-container">
<label :for="`switch-button-${randomId}`" class="label">{{ label }}</label>
<label v-if="smallLabel" :for="`switch-button-${randomId}`" class="small-label">{{
smallLabel
}}</label>

<label
v-if="smallLabel && smallLabelNegative && !value"
:for="`switch-button-${randomId}`"
class="small-label"
>
{{ smallLabelNegative }}
</label>
<label v-else-if="smallLabel" :for="`switch-button-${randomId}`" class="small-label">
{{ smallLabel }}
</label>
</div>
</div>
</template>
Expand All @@ -33,7 +41,13 @@ export default defineComponent({
label: String,
smallLabel: {
type: String,
required: false
required: false,
default: null
},
smallLabelNegative: {
type: String,
required: false,
default: null
},
disabled: Boolean,
right: {
Expand Down Expand Up @@ -176,7 +190,9 @@ export default defineComponent({
background-color: var(--theme-color);
display: block;
position: absolute;
transition: background-color 300ms $intro-easing, left 300ms $overshoot-easing;
transition:
background-color 300ms $intro-easing,
left 300ms $overshoot-easing;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/components/form/LoginForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<InformationHint class="hint">Usernames are case sensitive</InformationHint>
<Spinner />
<form id="login" method="post" @submit.prevent="login">
<FormInput :id="'username'" v-model="username" :label="'username'" :type="'username'" />
<FormInput :id="'username'" v-model="username" :label="'username'" :type="'username'" autofocus />
<FormInput :id="'password'" v-model="password" :label="'password'" :type="'password'" />
<SubmitButton :label="'Sign in'" />
</form>
Expand Down
2 changes: 1 addition & 1 deletion client/components/form/RegisterForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ captchaStore.getCaptcha();
<InformationHint class="hint">Usernames are case sensitive</InformationHint>
<Spinner />
<form id="register" ref="registerForm" method="post" @submit.prevent="register">
<FormInput :id="'username'" v-model="username" :label="'username'" :type="'username'" />
<FormInput :id="'username'" v-model="username" :label="'username'" :type="'username'" autofocus />
<FormInput :id="'password'" v-model="password" :label="'password'" :type="'password'" />
<FormInput
:id="'repeat-password'"
Expand Down
13 changes: 5 additions & 8 deletions client/components/header/UserMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
>Sign in</a
>
<a
v-show="!userAuthenticated"
v-show="!userAuthenticated && registrationEnabled"
id="register"
v-tippy="'Sign up'"
:href="`/register${currentPageRef('register')}`"
Expand Down Expand Up @@ -100,7 +100,7 @@
<div class="menu-btn-content"><VTIcon name="mdi:account-circle" />Sign in</div>
</a>
<a
v-show="!userAuthenticated"
v-show="!userAuthenticated && registrationEnabled"
id="register-btn"
v-tippy="'Sign up'"
:href="`/register${currentPageRef('register')}`"
Expand Down Expand Up @@ -170,6 +170,7 @@ export default defineComponent({
const { apiUrl } = useApiUrl();
const router = useRouter();
const { registrationEnabled } = useRegistrationEnabled();
const accountMenuVisible = ref(false);
const settingsOpen = ref(false);
Expand Down Expand Up @@ -248,10 +249,6 @@ export default defineComponent({
router.push(`/register${currentPageRef('register')}`);
closeAllPopups();
};
const logout = (): void => {
userStore.logout();
closeAllPopups();
};
const onEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
Expand Down Expand Up @@ -290,13 +287,13 @@ export default defineComponent({
openSubscriptions,
getProfileImageUrl,
login,
logout,
register,
loginOpen,
registerOpen,
onLoginClick,
onRegisterClick,
userStore
userStore,
registrationEnabled
};
}
});
Expand Down
28 changes: 25 additions & 3 deletions client/composables/api/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ export const useGetAdminInfo = () => {
const { apiUrl } = useApiUrl();
const { vtFetch } = useVtFetch();

return useLazyAsyncData<ApiDto<'InfoDto'>>(
'admin-info',
() => vtFetch(`${apiUrl.value}admin/info`),
return useLazyAsyncData('admin-info', () => vtFetch(`${apiUrl.value}admin/info`), {
server: false
});
};

export const useGetServerSettings = () => {
const { apiUrl } = useApiUrl();
const { vtFetch } = useVtFetch();

return useLazyAsyncData(
'server-settings',
() => vtFetch<ApiDto<'ServerSettingsDto'>>(`${apiUrl.value}admin/server-settings`),
{
server: false
}
Expand All @@ -36,3 +45,16 @@ export const useGetBlockedVideos = () => {
}
);
};

export const useCreateUser = () => {
const { apiUrl } = useApiUrl();
const { vtFetch } = useVtFetch();

const createUser = async (data: { username: string; password: string }) => {
return vtFetch<ApiDto<'UserprofileDto'>>(`${apiUrl.value}admin/users`, {
method: 'POST',
body: data
});
};
return { createUser };
};
6 changes: 6 additions & 0 deletions client/composables/registrationEnabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const useRegistrationEnabled = () => {
const config = useRuntimeConfig();
return {
registrationEnabled: config.public.registrationEnabled
};
};
2 changes: 1 addition & 1 deletion client/composables/vtFetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const useVtFetch = () => {
requestOptions.headers = { ...requestOptions.headers, cookie: cookieHeader };
}

if (process.server && !options?.external && global.nestApp) {
if (process.server && !options?.external && global?.nestApp) {
const response = await global.nestApp.inject({
method: (requestOptions.method ?? 'GET') as HTTPMethods,
url: request.toString(),
Expand Down
Loading

0 comments on commit 7131c8c

Please sign in to comment.