Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fb82c62
Removed https part of the authurl variable. Instead put it when used …
Darkdusk234 Feb 26, 2026
c8e6a04
Fixed a problem with auth server when running locally
Darkdusk234 Feb 26, 2026
6ff6043
Fixed problem with running websocket on localhost
Darkdusk234 Feb 26, 2026
77d84cd
Fixed problem with login not working and not giving correct status code
Darkdusk234 Feb 27, 2026
4cf33f1
Uppdated error message to not be specific in what was wrong with logi…
Darkdusk234 Mar 2, 2026
5236393
Removed guest version of endpoint variable since it the original now …
Darkdusk234 Mar 2, 2026
b7e82fc
Fixed problem with fileserver not working with localhost
Darkdusk234 Mar 5, 2026
1f8e93d
Updated code so it should work if program is run on either localhost …
Darkdusk234 Mar 9, 2026
523ef94
Merge pull request #153 from Darkdusk234/dev
gunhaxxor Mar 10, 2026
67671b7
adjustments of local vs https
gunhaxxor Mar 10, 2026
43dc67f
make local env var exposed to frontend
gunhaxxor Mar 12, 2026
b2ea938
updated the env example
gunhaxxor Mar 13, 2026
82c80f0
Nicer default project name
gunhaxxor Mar 13, 2026
72cf56b
no tls shit in caddyfile. Let that stuff do it's own magic!
gunhaxxor Mar 13, 2026
980ba00
missed several env updates in the codes!!! gah!
gunhaxxor Mar 13, 2026
c797406
Merge pull request #1 from immersed-web/dev
Darkdusk234 Apr 2, 2026
9e774d7
Added unique error code and message if error is caused by username al…
Darkdusk234 Apr 13, 2026
d2498fb
Added message that shows if creating a user was successful or if some…
Darkdusk234 Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,35 @@
(production) {
root * app/dist # will be used as the start directory for the ditrectives below
try_files {path} /index.html # catch all (non existent files) redirect for SPA frontend
file_server # serve the files!
file_server # serve the app files!
}

(production_tls) {
# log
}
# (online_tls) {
# # log
# }

(development_tls) {
tls ./certs/localhost+2.pem ./certs/localhost+2-key.pem
}
# (local_tls) {
# tls ./certs/localhost+2.pem ./certs/localhost+2-key.pem
# }

# @isDev expression `{$ENVIRONMENT} == development`

{$EXPOSED_SERVER_URL} {
import `{$ENVIRONMENT:production}_tls`
# This does not work currently because the variable is a true or false rather than an actual value
# import `{$EXPOSED_LOCAL:online}_tls`

# tls ./certs/localhost+2.pem ./certs/localhost+2-key.pem
# Important to use the route directive so that the reverse_proxy is matched first
# (default is try_files before reverse_proxy)
route {
handle_path /auth* {
reverse_proxy localhost:{$AUTH_PORT}
reverse_proxy localhost:{$EXPOSED_AUTH_PORT}
}
handle_path /socket* {
reverse_proxy localhost:{$MEDIASOUP_PORT}
reverse_proxy localhost:{$EXPOSED_MEDIASOUP_PORT}
}
handle_path /files* {
reverse_proxy localhost:{$FILESERVER_PORT}
reverse_proxy localhost:{$EXPOSED_FILESERVER_PORT}
}

import {$ENVIRONMENT:production}
Expand Down
24 changes: 17 additions & 7 deletions app/src/modules/authClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@ import type { JwtPayload, JwtUserData, UserRole } from 'schemas';
import type { User } from 'database/schema';
import decodeJwt from 'jwt-decode';

const completeAuthUrl = `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_AUTH_PATH}`;
// console.log('authUrl: ', completeAuthUrl);
const devMode = import.meta.env.DEV;
const localMode = import.meta.env.EXPOSED_LOCAL === 'true';
console.log('localMode:', localMode);

let completeAuthUrl: string;
if (localMode) {
completeAuthUrl = `http://${import.meta.env.EXPOSED_SERVER_URL}:${import.meta.env.EXPOSED_AUTH_PORT}`;
} else {
completeAuthUrl = `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_AUTH_PATH}`;
}

console.log('authUrl: ', completeAuthUrl);
const authEndpoint = axios.create({ baseURL: completeAuthUrl, withCredentials: true });

export function createUser(username: string, password: string, role: UserRole) {
Expand Down Expand Up @@ -37,7 +47,7 @@ export function updateUser(userData: {userId: string, username?: string, passwor
}

export function deleteUser(userId: string) {
return handleResponse(() => authEndpoint.post('/user/delete', {userId}));
return handleResponse(() => authEndpoint.post('/user/delete', { userId }));
}

type FetchedUsers = Omit<User, 'password'>[]
Expand Down Expand Up @@ -186,15 +196,15 @@ export const loginWithAutoToken = async (username: string, password: string) =>

export const guestJwt = (params?: {requestedUsername?: string, previousToken?: string}) => {
if(params?.previousToken){
return handleResponse<string>(() =>authEndpoint.get(`/guest-jwt?prevToken=${params.previousToken}`));
return handleResponse<string>(() => authEndpoint.get(`/guest-jwt?prevToken=${params.previousToken}`));
}
if(params?.requestedUsername){
return handleResponse<string>(() =>authEndpoint.get(`/guest-jwt?username=${params.requestedUsername}`));
return handleResponse<string>(() => authEndpoint.get(`/guest-jwt?username=${params.requestedUsername}`));
}
return handleResponse<string>(() =>authEndpoint.get('/guest-jwt'));
return handleResponse<string>(() => authEndpoint.get('/guest-jwt'));
};
export const getJwt = () => handleResponse<string>(() => authEndpoint.get('user/jwt'));
export const getMe = () => handleResponse<JwtUserData>(() => authEndpoint.get('/user/me'));
export const getMe = () => handleResponse<JwtUserData>(() => authEndpoint.get('user/me'));
export const logout = () => {
clearTimeout(activeTimeout);
return handleResponse<void>(() => authEndpoint.get('user/logout'));
Expand Down
6 changes: 5 additions & 1 deletion app/src/modules/trpcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ import { createReceiver } from 'ts-event-bridge/receiver'
import { shallowRef, type ShallowRef, computed, type ComputedRef } from 'vue';
import type { Payload } from 'ts-event-bridge/sender';

const wsBaseURL = `wss://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_MEDIASOUP_PATH}`;
const localMode = import.meta.env.EXPOSED_LOCAL === 'true';

let wsBaseURL = `wss://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_MEDIASOUP_PATH}`;
if (localMode) {
wsBaseURL = `ws://${import.meta.env.EXPOSED_SERVER_URL}:${import.meta.env.EXPOSED_MEDIASOUP_PORT}`;
}
const { receiver, onMessageReceived } = createReceiver<UserClientEventMap>({
onUnhandledEvent(evt, msg) {
console.warn('unhandled event: ', evt, msg);
Expand Down
17 changes: 14 additions & 3 deletions app/src/modules/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,28 @@ import axios, { CanceledError, type AxiosProgressEvent } from "axios";
import type { UploadResponse } from "fileserver";
import type { AssetId } from "schemas";

const devMode = import.meta.env.DEV;
const localMode = import.meta.env.EXPOSED_LOCAL === 'true';

export function getAssetUrl<T extends string>(generatedName: T) {
// console.log('getAssetUrl called', generatedName);
// if (generatedName === undefined) {
// return generatedName
// }

return `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_FILESERVER_PATH}/file/${generatedName}`;
// return `https://${process.env.EXPOSED_SERVER_URL}${process.env.EXPOSED_FILESERVER_PATH}/files/${generatedName}`;
if (localMode) {
return `http://${import.meta.env.EXPOSED_SERVER_URL}:${import.meta.env.EXPOSED_FILESERVER_PORT}/file/${generatedName}`;
} else {
return `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_FILESERVER_PATH}/file/${generatedName}`;
}
}

const fileserverUrl = `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_FILESERVER_PATH}` as const
let fileserverUrl: string;
if (localMode) {
fileserverUrl = `http://${import.meta.env.EXPOSED_SERVER_URL}:${import.meta.env.EXPOSED_FILESERVER_PORT}`;
} else {
fileserverUrl = `https://${import.meta.env.EXPOSED_SERVER_URL}${import.meta.env.EXPOSED_FILESERVER_PATH}`;
}
export const assetsUrl = `${fileserverUrl}/file/` as const;
export async function uploadFileData({ data, authToken, onProgress, abortController }: { data: FormData, authToken: string, onProgress?: (progressEvent: AxiosProgressEvent) => void, abortController?: AbortController }) {
// We can apparently receive upload progress after the upload is actually finished.
Expand Down
50 changes: 43 additions & 7 deletions app/src/views/admin/AdminUserManagerView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
</button>
</div>
</div>
<div v-if="responseMessage" class="alert mb-4" :class="responseType === 'alert-success' ? 'alert-success' : 'alert-error'">
<span>{{ responseMessage }}</span>
</div>
<h3>
Befintliga användare
</h3>
Expand Down Expand Up @@ -92,7 +95,7 @@
class="btn">
<span class="material-icons">edit</span>
</button>
<button @click="makeCallThenResetList(() => deleteUser(user.userId))" class="btn btn-error">
<button @click="makeCallThenResetList(() => deleteUser(user.userId), false)" class="btn btn-error">
<span class="material-icons">delete</span>
</button>
</template>
Expand Down Expand Up @@ -143,6 +146,10 @@ function getClassForRole(role: UserRole) {
}
}

const responseMessage = ref<string>();
const responseType = ref<'alert-success' | 'alert-error'>('alert-success');
const messageTimeoutId = ref<NodeJS.Timeout | null>(null);

const createdUsername = ref('');
const createdPassword = ref('');
const createdRole = ref<UserRole>('guest');
Expand Down Expand Up @@ -209,12 +216,41 @@ onBeforeMount(async () => {
console.log(fetchedUsers.value);
});

async function makeCallThenResetList(fetchReq: (...p: any) => Promise<any>) {
await fetchReq();
editedUserId.value = undefined;
createdUsername.value = '';
createdPassword.value = '';
fetchedUsers.value = await getUsers();
async function makeCallThenResetList(
fetchReq: (...p: any) => Promise<any>,
showMessageBool: boolean = true) {
try {
await fetchReq();

if (showMessageBool) {
showMessage(`Användare "${createdUsername.value}" skapad!`, 'alert-success');
}
createdUsername.value = '';
createdPassword.value = '';
createdRole.value = 'guest';
} catch (e: any) {
if (e.message === 'username already exists') {
showMessage('Användarnamnet finns redan.', 'alert-error');
} else {
showMessage(`Fel: ${e.message || 'Okänt fel vid skapande av användare'}`, 'alert-error');
}
} finally {
fetchedUsers.value = await getUsers();
editedUserId.value = undefined;
}
}

function showMessage(msg: string, type: 'alert-success' | 'alert-error') {
responseMessage.value = msg;
responseType.value = type;

if (messageTimeoutId.value) {
clearTimeout(messageTimeoutId.value);
}

messageTimeoutId.value = setTimeout(() => {
responseMessage.value = '';
}, 5000);
}

</script>
20 changes: 10 additions & 10 deletions backend/auth/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const haikunator = new Haikunator({

// console.log('environment: ', process.env);
const devMode = process.env.DEVELOPMENT;
const localMode = process.env.EXPOSED_LOCAL === 'true';

const app = express();

Expand All @@ -32,11 +33,11 @@ app.set('trust proxy', 1);
// Find a way to make our auth flow work without this set to false
let cookieHttpOnly = false;
let cookieSecure = true;
if (devMode) {
let originUrls: string[] = [];
if (localMode) {
// NOTE: I couldnt come up with a way to allow all origins so we have hardcoded the devservers url here
const devServerUrl = 'http://localhost:5173';
console.log('allowing cors for development: ', devServerUrl);
app.use(cors({ credentials: true, origin: [devServerUrl] }));
originUrls = ['http://localhost:5173', 'http://127.0.0.1:5173'];
console.log('allowing cors for local development: ', originUrls);

console.log('allowing cookie despite not https');
cookieHttpOnly = false;
Expand All @@ -46,13 +47,12 @@ if (devMode) {
console.error('no EXPOSED_SERVER_URL provided from env');
process.exit(1);
}
console.log('restricting CORS for production');
app.use(cors({
origin: [process.env.EXPOSED_SERVER_URL],
credentials: true
}));
originUrls = [`https://${process.env.EXPOSED_SERVER_URL}`];
console.log('restricting CORS for https:', originUrls);
}

app.use(cors({ credentials: true, origin: originUrls }));

app.use((req, res, next) => {
return parseJsonBody()(req, res, (err) => {
if (err) {
Expand Down Expand Up @@ -167,7 +167,7 @@ app.get('/guest-jwt', (req, res) => {
});


const port = Number.parseInt(process.env.AUTH_PORT || '3333');
const port = Number.parseInt(process.env.EXPOSED_AUTH_PORT || '3333');
app.listen(port, () => {
console.log(`listening on ${port}`);
if (process.env.DEVELOPMENT)
Expand Down
21 changes: 17 additions & 4 deletions backend/auth/src/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ const createUser: RequestHandler = async (req: CreateUserRequest, res) => {
res.status(201).send(dbResponse);
return;
} catch (e) {
//Checking if error is from unique constraint violation on username
if (typeof e === 'object' && e !== null && 'code' in e && (e as { code?: string }).code === '23505') {
res.status(409).send('username already exists');
return;
}

console.error('database error when creating user');
console.error(e);
res.status(501).send(e);
Expand All @@ -105,6 +111,7 @@ interface CreateSenderRequest extends ExpressReq {
streamId?: StreamId,
}
}

const createSenderForVenue: RequestHandler = async (req : CreateSenderRequest, res) => {
const userData = req.session.user
// console.log(userData);
Expand Down Expand Up @@ -147,6 +154,7 @@ interface UpdateUserRequest extends ExpressReq {
password?: string,
}
}

const updateUser: RequestHandler = async (req: UpdateUserRequest, res) => {
const userData = req.session.user;
const payload = req.body;
Expand Down Expand Up @@ -206,6 +214,7 @@ interface DeleteUserRequest extends ExpressReq {
userId: UserId
}
}

const deleteUser: RequestHandler = async (req: DeleteUserRequest, res) => {
try {
const userData = req.session.user;
Expand Down Expand Up @@ -397,7 +406,7 @@ const loginUser: RequestHandler = async (req, res) => {
where: eq(schema.users.username, username),
})
if (!foundUser) {
throw new Error('no user with that username found!');
throw new Error('no user with that username and password found!');
}
const correct = await bcrypt.compare(password, foundUser.password);
if (correct) {
Expand All @@ -406,14 +415,18 @@ const loginUser: RequestHandler = async (req, res) => {
req.session.user = pickedUserData;
res.status(200).send();
return;
} else {
throw new Error('no user with that username and password found!');
}
} catch (e) {
console.error(e);
res.status(501).send('failed when trying to login');
if (e instanceof Error && e.message === 'no user with that username and password found!') {
res.status(401).send('User with that username or password not found');
} else {
res.status(501).send('failed when trying to login');
}
return;
}
res.status(403).send('You shall not pass!');
return;

};

Expand Down
Loading